authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-02-17 00:42:05-08:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-02-18 02:46:06-08:00
logc2487692e6d51da16194cd38ba2b42f095ee7594
treec6977c291b145699061208614582e03305a9cf1e
parente8d9aa0ccc17ba1add4757022b7121d0f731858c
signaturebadge-check Signed by SSH key SHA256:xbd+BjjhyBfwk7GVoURf9Yx0gzDerHbvYv7SddNWmAs

chore: fork markdown-it to fix a bug


106 files changed, 36030 insertions(+), 13 deletions(-)

ARCHITECTURE.md+9-2
...@@ -1,16 +1,23 @@...@@ -1,16 +1,23 @@
1# architecture1# architecture
22
3markodown is implemented in rust using a fork of the markdown-it-rust create. we
4fork it because they are unmaintained and we have hit bugs. the rust codebase is
5compiled to wasm and then shipped as an NPM/JSR package for use in the web
6ecosystem.
7
3## `src/`: rust code8## `src/`: rust code
49
5the rust code in `src` implements most of markodown. it exports a cli for10the rust code in `src` implements most of markodown. it exports a cli for
6testing. most of the tests are on the rust code11testing. most of the tests are on the rust code. here are the relevant files
712
8- `src/lib.rs` - main library, primary function `transform`13- `src/lib.rs` - main library, primary function `transform`
9- `src/main.rs` - main cli14 - `src/wasm.rs` - binding for WASM
15- `src/main.rs` - testing cli
10- `src/typescript.rs` - wrappers around oxc parser to expose parsers for16- `src/typescript.rs` - wrappers around oxc parser to expose parsers for
11 different subsets of the ast, including a much more garbage-forgiving17 different subsets of the ast, including a much more garbage-forgiving
12 expression parser.18 expression parser.
13- `src/marko.rs` - general marko tag parser19- `src/marko.rs` - general marko tag parser
20 - `src/marko_ast.rs` - ast structure for the marko parser
14- `src/plugin/` - `markdown-it` plugin and all rules21- `src/plugin/` - `markdown-it` plugin and all rules
1522
16markdown-it does not support failiable plugins, so instead errors are lowered23markdown-it does not support failiable plugins, so instead errors are lowered
Cargo.lock+1-10
...@@ -82,12 +82,6 @@ dependencies = [...@@ -82,12 +82,6 @@ dependencies = [
82 "object",82 "object",
83]83]
8484
85[[package]]
86name = "argparse"
87version = "0.2.2"
88source = "registry+https://github.com/rust-lang/crates.io-index"
89checksum = "3f8ebf5827e4ac4fd5946560e6a99776ea73b596d80898f357007317a7141e47"
90
91[[package]]85[[package]]
92name = "autocfg"86name = "autocfg"
93version = "1.5.0"87version = "1.5.0"
...@@ -379,11 +373,8 @@ dependencies = [...@@ -379,11 +373,8 @@ dependencies = [
379373
380[[package]]374[[package]]
381name = "markdown-it"375name = "markdown-it"
382version = "0.6.1"376version = "0.6.1-patched"
383source = "registry+https://github.com/rust-lang/crates.io-index"
384checksum = "f99c010929c8217b2dc0940954267a2e15a15f17cb309cd1f299e21933f84fac"
385dependencies = [377dependencies = [
386 "argparse",
387 "const_format",378 "const_format",
388 "derivative",379 "derivative",
389 "derive_more",380 "derive_more",
Cargo.toml+5-1
...@@ -8,7 +8,7 @@ crate-type = ["cdylib", "rlib"]...@@ -8,7 +8,7 @@ crate-type = ["cdylib", "rlib"]
8[dependencies]8[dependencies]
9serde = { version = "1.0", features = ["derive"] }9serde = { version = "1.0", features = ["derive"] }
10clap = { version = "4.5.57", features = ["derive"] }10clap = { version = "4.5.57", features = ["derive"] }
11markdown-it = { version = "0.6.1", default-features = false, features = ["linkify"] }11markdown-it = { path = "crates/markdown-it", default-features = false, features = ["linkify"] }
12oxc_allocator = "0.96.0"12oxc_allocator = "0.96.0"
13oxc_ast = "0.96.0"13oxc_ast = "0.96.0"
14oxc_diagnostics = "0.96.0"14oxc_diagnostics = "0.96.0"
...@@ -18,3 +18,7 @@ serde_json = "1.0"...@@ -18,3 +18,7 @@ serde_json = "1.0"
18serde_yml = "0.0.12"18serde_yml = "0.0.12"
19wasm-bindgen = { version = "0.2" }19wasm-bindgen = { version = "0.2" }
20serde-wasm-bindgen = "0.6.5"20serde-wasm-bindgen = "0.6.5"
21
22[workspace]
23members = [".", "crates/markdown-it"]
24resolver = "2"
crates/markdown-it/Cargo.toml created+35
...@@ -0,0 +1,35 @@
1[package]
2name = "markdown-it"
3version = "0.6.1-patched"
4authors = ["Alex Kocharin <alex@kocharin.ru>"]
5description = "Rust port of popular markdown-it.js library."
6homepage = "https://github.com/markdown-it-rust/markdown-it"
7repository = "https://github.com/markdown-it-rust/markdown-it"
8readme = "README.md"
9keywords = ["markdown", "commonmark"]
10license = "MIT"
11categories = ["text-processing", "parsing"]
12edition = "2021"
13
14[lib]
15name = "markdown_it"
16path = "src/lib.rs"
17
18[features]
19default = ["linkify"]
20syntect = []
21
22[dependencies]
23const_format = ">= 0.1.0, < 0.3"
24derivative = ">= 1.0.2, < 3"
25derive_more = ">= 0.99.0, < 1"
26downcast-rs = ">= 1.0.2, < 2"
27entities = ">= 0.1.0, < 2"
28html-escape = ">= 0.1.0, < 0.3"
29linkify = { version = ">= 0.5.0, < 0.11", optional = true }
30mdurl = ">= 0.3.1, < 0.4"
31once_cell = ">= 1.0.1, < 2"
32readonly = ">= 0.2.0, < 0.3"
33regex = ">= 1.0.0, < 2"
34stacker = ">= 0.1.2, < 0.2"
35unicode-general-category = ">= 0.1.0, < 0.7"
crates/markdown-it/LICENSE created+28
...@@ -0,0 +1,28 @@
1The MIT License
2
3Rust port https://github.com/rlidwka/markdown-it.rs:
4Copyright (c) 2022 Alex Kocharin.
5
6Original library https://github.com/markdown-it/markdown-it:
7Copyright (c) 2014 Vitaly Puzrin, Alex Kocharin.
8
9Permission is hereby granted, free of charge, to any person
10obtaining a copy of this software and associated documentation
11files (the "Software"), to deal in the Software without
12restriction, including without limitation the rights to use,
13copy, modify, merge, publish, distribute, sublicense, and/or sell
14copies of the Software, and to permit persons to whom the
15Software is furnished to do so, subject to the following
16conditions:
17
18The above copyright notice and this permission notice shall be
19included in all copies or substantial portions of the Software.
20
21THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
23OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28OTHER DEALINGS IN THE SOFTWARE.
crates/markdown-it/README.md created+32
...@@ -0,0 +1,32 @@
1# markdown-it
2
3This directory holds a fork of the unmaintained markdown-it-rust package.
4Markodown forks this package to make bug fixes to its core CommonMark
5implementation.
6
7Rust port of popular
8[markdown-it.js](https://github.com/markdown-it/markdown-it) library.
9
10### Features
11
12- 100% CommonMark compatibility
13- AST
14- Source maps (full support, not just on block tags like cmark)
15- Ability to write your own syntax of arbitrary complexity
16 - to prove this point, CommonMark syntax itself is written as a plugin
17
18### Usage
19
20```rust
21let parser = &mut markdown_it::MarkdownIt::new();
22markdown_it::plugins::cmark::add(parser);
23markdown_it::plugins::extra::add(parser);
24
25let ast = parser.parse("Hello **world**!");
26let html = ast.render();
27
28print!("{html}");
29// prints "<p>Hello <strong>world</strong>!</p>"
30```
31
32For a guide on how to extend it, see `examples` folder.
crates/markdown-it/examples/ferris/README.md created+21
...@@ -0,0 +1,21 @@
1This is an example of how you can make your own plugins in markdown-it.
2
3### What is it
4
5There are 3 different plugins here:
6
7- inline rule - turns `(\/)` into `🦀` in *inline context* (i.e. inside other text)
8
9- block rule - turns `(\/)-------(\/)` into
10 [ferris.svg](https://upload.wikimedia.org/wikipedia/commons/0/0f/Original_Ferris.svg)
11 in *block context* (i.e. it has to occupy the entire line)
12
13 - core rule - counts the number of nodes created by the above two plugins and writes
14 that number at the end of the document
15
16It represents three stages of markdown processing (block elements, inline elements
17and AST post-processing).
18
19### How to use
20
21`cargo run --example ferris`
crates/markdown-it/examples/ferris/block_rule.rs created+72
...@@ -0,0 +1,72 @@
1// Replaces `(\/)-------(\/)` with a nice picture.
2
3use markdown_it::parser::block::{BlockRule, BlockState};
4use markdown_it::{MarkdownIt, Node, NodeValue, Renderer};
5
6const CRAB_CLAW : &str = r#"(\/)"#;
7const CRAB_URL : &str = "https://upload.wikimedia.org/wikipedia/commons/0/0f/Original_Ferris.svg";
8
9#[derive(Debug)]
10// This is a structure that represents your custom Node in AST.
11pub struct BlockFerris;
12
13impl NodeValue for BlockFerris {
14 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
15 // build attributes for `div`
16 let mut attrs_div = node.attrs.clone();
17 attrs_div.push(("class", "ferris-block".into()));
18
19 // build attributes for `img`
20 let attrs_img = vec![("src", CRAB_URL.into())];
21
22 fmt.cr(); // linebreak, multiples get merged
23 fmt.open("div", &attrs_div); // opening tag, `<div>`
24 fmt.self_close("img", &attrs_img); // `<img>`
25 fmt.close("div"); // closing tag, `</div>`
26 fmt.cr();
27 }
28}
29
30// This is an extension for the block subparser.
31struct FerrisBlockScanner;
32
33impl BlockRule for FerrisBlockScanner {
34 // This is a custom function that will be invoked on every line
35 // in a block context.
36 //
37 // It should get a line number `state.line` and report if your
38 // custom structure appears there.
39 //
40 // If custom structure is found, it:
41 // - creates a new `Node` in AST
42 // - increments `state.line` to a position after this node
43 // - returns true
44 //
45 // In "silent mode" (when `silent=true`) you aren't allowed to
46 // create any nodes, should only increment `state.line`.
47 //
48 fn run(state: &mut BlockState) -> Option<(Node, usize)> {
49 // get contents of a line number `state.line` and check it
50 let line = state.get_line(state.line).trim();
51 if !line.starts_with(CRAB_CLAW) { return None; }
52 if !line.ends_with(CRAB_CLAW) { return None; }
53
54 // require any number of `-` in between, but no less than 4
55 if line.len() < CRAB_CLAW.len() * 2 + 4 { return None; }
56
57 // and make sure no other characters are present there
58 let dashes = &line[CRAB_CLAW.len()..line.len()-CRAB_CLAW.len()];
59 if dashes.chars().any(|c| c != '-') { return None; }
60
61 // return new node and number of lines it occupies
62 Some((
63 Node::new(BlockFerris),
64 1,
65 ))
66 }
67}
68
69pub fn add(md: &mut MarkdownIt) {
70 // insert this rule into block subparser
71 md.block.add_rule::<FerrisBlockScanner>();
72}
crates/markdown-it/examples/ferris/core_rule.rs created+64
...@@ -0,0 +1,64 @@
1// Counts the number of crabs lurking around.
2
3use super::block_rule::BlockFerris;
4use super::inline_rule::InlineFerris;
5use markdown_it::parser::core::CoreRule;
6use markdown_it::{MarkdownIt, Node, NodeValue, Renderer};
7
8#[derive(Debug)]
9// This is a structure that represents your custom Node in AST,
10// it has one single argument - crab counter.
11pub struct FerrisCounter(usize);
12
13// This defines how your custom node should be rendered.
14impl NodeValue for FerrisCounter {
15 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
16 // `node.attrs` are custom attributes added by other plugins
17 // (for example, source mapping information)
18 let mut attrs = node.attrs.clone();
19
20 // add a custom class attribute
21 attrs.push(("class", "ferris-counter".into()));
22
23 fmt.cr(); // linebreak, multiples get merged
24 fmt.open("footer", &attrs);
25 #[allow(clippy::useless_format)] // for simplicity's sake
26 fmt.text(&match self.0 {
27 0 => format!("No crabs around here."),
28 1 => format!("There is a crab lurking in this document."),
29 _ => format!("There are {} crabs lurking in this document.", self.0),
30 });
31 fmt.close("footer");
32 fmt.cr();
33 }
34}
35
36// This is an extension for the markdown parser.
37struct FerrisCounterRule;
38
39impl CoreRule for FerrisCounterRule {
40 // This is a custom function that will be invoked once per document.
41 //
42 // It has `root` node of the AST as an argument and may modify its
43 // contents as you like.
44 //
45 fn run(root: &mut Node, _: &MarkdownIt) {
46 let mut counter = 0;
47
48 // walk through AST recursively and count the number of two
49 // custom nodes added by other two rules
50 root.walk(|node, _| {
51 if node.is::<InlineFerris>() || node.is::<BlockFerris>() {
52 counter += 1;
53 }
54 });
55
56 // append a counter to the root as a custom node
57 root.children.push(Node::new(FerrisCounter(counter)))
58 }
59}
60
61pub fn add(md: &mut MarkdownIt) {
62 // insert this rule into parser
63 md.add_rule::<FerrisCounterRule>();
64}
crates/markdown-it/examples/ferris/inline_rule.rs created+61
...@@ -0,0 +1,61 @@
1// Replaces `(\/)` with `🦀`.
2
3use markdown_it::parser::inline::{InlineRule, InlineState};
4use markdown_it::{MarkdownIt, Node, NodeValue, Renderer};
5
6const CRAB_CLAW : &str = r#"(\/)"#;
7
8#[derive(Debug)]
9// This is a structure that represents your custom Node in AST.
10pub struct InlineFerris;
11
12// This defines how your custom node should be rendered.
13impl NodeValue for InlineFerris {
14 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
15 // `node.attrs` are custom attributes added by other plugins
16 // (for example, source mapping information)
17 let mut attrs = node.attrs.clone();
18
19 // add a custom class attribute
20 attrs.push(("class", "ferris-inline".into()));
21
22 fmt.open("span", &attrs);
23 fmt.text("🦀");
24 fmt.close("span");
25 }
26}
27
28// This is an extension for the inline subparser.
29struct FerrisInlineScanner;
30
31impl InlineRule for FerrisInlineScanner {
32 // This is a character that starts your custom structure
33 // (other characters may get skipped over).
34 const MARKER: char = '(';
35
36 // This is a custom function that will be invoked on every character
37 // in an inline context.
38 //
39 // It should look for `state.src` exactly at position `state.pos`
40 // and report if your custom structure appears there.
41 //
42 // If custom structure is found, it:
43 // - creates a new `Node` in AST
44 // - returns length of it
45 //
46 fn run(state: &mut InlineState) -> Option<(Node, usize)> {
47 let input = &state.src[state.pos..state.pos_max]; // look for stuff at state.pos
48 if !input.starts_with(CRAB_CLAW) { return None; } // return None if it's not found
49
50 // return new node and length of this structure
51 Some((
52 Node::new(InlineFerris),
53 CRAB_CLAW.len(),
54 ))
55 }
56}
57
58pub fn add(md: &mut MarkdownIt) {
59 // insert this rule into inline subparser
60 md.inline.add_rule::<FerrisInlineScanner>();
61}
crates/markdown-it/examples/ferris/main.rs created+31
...@@ -0,0 +1,31 @@
1// Adds three custom plugins and runs them.
2mod block_rule;
3mod core_rule;
4mod inline_rule;
5
6fn main() {
7 // create markdown parser
8 let md = &mut markdown_it::MarkdownIt::new();
9
10 // add commonmark syntax, you almost always want to do that
11 markdown_it::plugins::cmark::add(md);
12
13 // add custom three rules described above
14 inline_rule::add(md);
15 block_rule::add(md);
16 core_rule::add(md);
17
18 // and now you can use it
19 let html = md.parse(r#"
20(\/) hello world (\/)
21(\/)-------------(\/)
22 "#).render();
23
24 print!("{html}");
25
26 assert_eq!(html.trim(), r#"
27<p><span class="ferris-inline">🦀</span> hello world <span class="ferris-inline">🦀</span></p>
28<div class="ferris-block"><img src="https://upload.wikimedia.org/wikipedia/commons/0/0f/Original_Ferris.svg"></div>
29<footer class="ferris-counter">There are 3 crabs lurking in this document.</footer>
30 "#.trim());
31}
crates/markdown-it/src/common/mod.rs created+11
...@@ -0,0 +1,11 @@
1//! Self-contained modules used for miscellaneous purposes.
2//!
3//! These are all candidates for being separated into different crates,
4//! tell me if functionality they provide is useful enough to do that.
5
6pub mod ruler;
7pub mod sourcemap;
8pub mod utils;
9
10mod typekey;
11pub use typekey::TypeKey;
crates/markdown-it/src/common/ruler.rs created+393
...@@ -0,0 +1,393 @@
1//! Plugin manager with dependency resolution.
2
3use derivative::Derivative;
4use once_cell::sync::OnceCell;
5use std::collections::{HashMap, HashSet};
6use std::fmt::Debug;
7use std::hash::Hash;
8use std::slice::Iter;
9
10///
11/// Ruler allows you to implement a plugin system with dependency management and ensure that
12/// your dependencies are called in the correct order.
13///
14/// You can use it like this:
15/// ```
16/// use markdown_it::common::ruler::Ruler;
17///
18/// // this example prints "[ hello, world! ]",
19/// // where each token is printed by separate closure
20/// let mut chain = Ruler::<&str, fn (&mut String)>::new();
21///
22/// // define rules printing "hello" and "world"
23/// chain.add("hello", |s| s.push_str("hello"));
24/// chain.add("world", |s| s.push_str("world"));
25///
26/// // open bracket should be before "hello", and closing one after "world"
27/// chain.add("open_bracket", |s| s.push_str("[ ")).before("hello");
28/// chain.add("close_bracket", |s| s.push_str(" ]")).after("world");
29///
30/// // between "hello" and "world" we shall have a comma
31/// chain.add("comma", |s| s.push_str(", ")).after("hello").before("world");
32///
33/// // after "world" we should have "!" as a first rule, but ensure "world" exists first
34/// chain.add("bang", |s| s.push_str("!")).require("world").after("world").before_all();
35///
36/// // now we run this chain
37/// let mut result = String::new();
38/// for f in chain.iter() { f(&mut result); }
39/// assert_eq!(result, "[ hello, world! ]");
40/// ```
41///
42/// This data structure contains any number of elements (M, T), where T is any type and
43/// M (mark) is its identifier.
44///
45/// - `M` is used for ordering and dependency checking, it must implement `Eq + Copy + Hash + Debug`
46/// . Common choices for `M` are `u32`, `&'static str`, or a special `Symbol` type
47/// designed for this purpose.
48///
49/// - `T` is any user-defined type. It's usually a function or boxed trait.
50///
51pub struct Ruler<M, T> {
52 deps: Vec<RuleItem<M, T>>,
53 compiled: OnceCell<(Vec<usize>, Vec<T>)>,
54}
55
56impl<M, T> Ruler<M, T> {
57 pub fn new() -> Self {
58 Self::default()
59 }
60}
61
62impl<M: Eq + Hash + Copy + Debug, T: Clone> Ruler<M, T> {
63 /// Add a new rule identified by `mark` with payload `value`.
64 pub fn add(&mut self, mark: M, value: T) -> &mut RuleItem<M, T> {
65 self.compiled = OnceCell::new();
66 let dep = RuleItem::new(mark, value);
67 self.deps.push(dep);
68 self.deps.last_mut().unwrap()
69 }
70
71 /// Remove all rules identified by `mark`.
72 pub fn remove(&mut self, mark: M) {
73 self.deps.retain(|dep| !dep.marks.contains(&mark));
74 }
75
76 /// Check if there are any rules identified by `mark`.
77 pub fn contains(&mut self, mark: M) -> bool {
78 self.deps.iter().any(|dep| dep.marks.contains(&mark))
79 }
80
81 /// Ordered iteration through rules.
82 #[inline]
83 pub fn iter(&self) -> Iter<T> {
84 self.compiled.get_or_init(|| self.compile()).1.iter()
85 }
86
87 fn compile(&self) -> (Vec<usize>, Vec<T>) {
88 // ID -> [RuleItem index]
89 let mut idhash = HashMap::<M, Vec<usize>>::new();
90
91 // RuleItem index -> [RuleItem index] - dependency graph, None if already inserted
92 let mut deps_graph = vec![HashSet::new(); self.deps.len()];
93
94 // additional level of indirection that takes into account item priority
95 let mut deps_order = vec![];
96 let mut beforeall_len = 0;
97 let mut afterall_len = 0;
98
99 // compiled result
100 let mut result = vec![];
101 let mut result_idx = vec![];
102
103 // track which rules have been added already
104 let mut deps_inserted = vec![false; self.deps.len()];
105 let mut deps_remaining = self.deps.len();
106
107 for (idx, dep) in self.deps.iter().enumerate() {
108 match dep.prio {
109 RuleItemPriority::Normal => {
110 deps_order.insert(deps_order.len() - afterall_len, idx);
111 }
112 RuleItemPriority::BeforeAll => {
113 deps_order.insert(beforeall_len, idx);
114 beforeall_len += 1;
115 }
116 RuleItemPriority::AfterAll => {
117 deps_order.insert(deps_order.len(), idx);
118 afterall_len += 1;
119 }
120 }
121 for mark in &dep.marks {
122 idhash.entry(*mark).or_default().push(idx);
123 }
124 }
125
126 // build dependency graph, replacing all after's with before's,
127 // i.e. B.after(A) -> A.before(B)
128 for idx in deps_order.iter().copied() {
129 let dep = self.deps.get(idx).unwrap();
130 for constraint in &dep.cons {
131 match constraint {
132 RuleItemConstraint::Before(v) => {
133 for depidx in idhash.entry(*v).or_default().iter() {
134 deps_graph.get_mut(*depidx).unwrap().insert(idx);
135 }
136 }
137 RuleItemConstraint::After(v) => {
138 for depidx in idhash.entry(*v).or_default().iter() {
139 deps_graph.get_mut(idx).unwrap().insert(*depidx);
140 }
141 }
142 RuleItemConstraint::Require(v) => {
143 assert!(
144 idhash.contains_key(v),
145 "missing dependency: {:?} requires {:?}", dep.marks.first().unwrap(), v
146 );
147 }
148 }
149 }
150 }
151
152 // now go through the deps and push whatever doesn't have any
153 'outer: while deps_remaining > 0 {
154 for idx in deps_order.iter().copied() {
155 let inserted = deps_inserted.get_mut(idx).unwrap();
156 if *inserted { continue; }
157
158 let dlist = deps_graph.get(idx).unwrap();
159 if dlist.is_empty() {
160 let dep = self.deps.get(idx).unwrap();
161 result.push(dep.value.clone());
162 result_idx.push(idx);
163 *inserted = true;
164 deps_remaining -= 1;
165 for d in deps_graph.iter_mut() {
166 d.remove(&idx);
167 }
168 continue 'outer;
169 }
170 }
171
172 #[cfg(debug_assertions)] {
173 // check cycles in dependency graph;
174 // this is very suboptimal, but only used to generate a nice panic message.
175 // in release mode we'll just simply panic
176 for idx in deps_order.iter().copied() {
177 let mut seen = HashMap::new();
178 let mut vec = vec![idx];
179 while let Some(didx) = vec.pop() {
180 let dlist = deps_graph.get(didx).unwrap();
181 for x in dlist.iter() {
182 if seen.contains_key(x) { continue; }
183 vec.push(*x);
184 seen.insert(*x, didx);
185 if *x == idx {
186 let mut backtrack = vec![];
187 let mut curr = idx;
188 while !backtrack.contains(&curr) {
189 backtrack.push(curr);
190 curr = *seen.get(&curr).unwrap();
191 }
192 backtrack.push(curr);
193 let path = backtrack.iter()
194 .rev()
195 .map(|x| format!("{:?}", self.deps.get(*x).unwrap().marks.first().unwrap()))
196 .collect::<Vec<String>>()
197 .join(" < ");
198 panic!("cyclic dependency: {}", path);
199 }
200 }
201 }
202 }
203 }
204
205 // if you see this in debug mode, report it as a bug
206 panic!("cyclic dependency: (use debug mode for more details)");
207 }
208
209 (result_idx, result)
210 }
211}
212
213impl<M: Eq + Hash + Copy + Debug, T: Clone> Debug for Ruler<M, T> {
214 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
215 let vec: Vec<(usize, M)> = self.compiled.get_or_init(|| self.compile()).0
216 .iter()
217 .map(|idx| (*idx, *self.deps.get(*idx).unwrap().marks.first().unwrap()))
218 .collect();
219
220 f.debug_struct("Ruler")
221 .field("deps", &self.deps)
222 .field("compiled", &vec)
223 .finish()
224 }
225}
226
227impl<M, T> Default for Ruler<M, T> {
228 fn default() -> Self {
229 Self {
230 deps: Vec::new(),
231 compiled: OnceCell::new(),
232 }
233 }
234}
235
236///
237/// Result of [Ruler::add](Ruler::add), allows to customize position of each rule.
238///
239#[derive(Derivative)]
240#[derivative(Debug)]
241pub struct RuleItem<M, T> {
242 marks: Vec<M>,
243 #[derivative(Debug="ignore")]
244 value: T,
245 prio: RuleItemPriority,
246 cons: Vec<RuleItemConstraint<M>>,
247}
248
249impl<M, T> RuleItem<M, T> {
250 fn new(mark: M, value: T) -> Self {
251 Self {
252 marks: vec![mark],
253 value,
254 prio: RuleItemPriority::Normal,
255 cons: vec![],
256 }
257 }
258}
259
260impl<M: Copy, T> RuleItem<M, T> {
261 /// Make sure this rule will be inserted before any rule defined by `mark` (if such rule exists).
262 /// ```
263 /// use markdown_it::common::ruler::Ruler;
264 /// let mut chain = Ruler::<&str, fn (&mut String)>::new();
265 ///
266 /// chain.add("a", |s| s.push_str("bar"));
267 /// chain.add("b", |s| s.push_str("foo")).before("a");
268 ///
269 /// let mut result = String::new();
270 /// for f in chain.iter() { f(&mut result); }
271 /// assert_eq!(result, "foobar");
272 /// ```
273 pub fn before(&mut self, mark: M) -> &mut Self {
274 self.cons.push(RuleItemConstraint::Before(mark));
275 self
276 }
277
278 /// Make sure this rule will be inserted after any rule defined by `mark` (if such rule exists).
279 /// Similar to [RuleItem::before](RuleItem::before).
280 pub fn after(&mut self, mark: M) -> &mut Self {
281 self.cons.push(RuleItemConstraint::After(mark));
282 self
283 }
284
285 /// This rule will be inserted as early as possible, while still taking into account dependencies,
286 /// i.e. `.after(X).before_all()` causes this to be first rule after X.
287 /// ```
288 /// use markdown_it::common::ruler::Ruler;
289 /// let mut chain = Ruler::<&str, fn (&mut String)>::new();
290 ///
291 /// chain.add("a", |s| s.push_str("A"));
292 /// chain.add("c", |s| s.push_str("C")).after("a");
293 /// chain.add("b", |s| s.push_str("B")).after("a").before_all();
294 ///
295 /// let mut result = String::new();
296 /// for f in chain.iter() { f(&mut result); }
297 /// // without before_all order will be ACB
298 /// assert_eq!(result, "ABC");
299 /// ```
300 pub fn before_all(&mut self) -> &mut Self {
301 self.prio = RuleItemPriority::BeforeAll;
302 self
303 }
304
305 /// This rule will be inserted as late as possible, while still taking into account dependencies,
306 /// i.e. `.before(X).after_all()` causes this to be last rule before X.
307 /// Similar to [RuleItem::before_all](RuleItem::before_all).
308 pub fn after_all(&mut self) -> &mut Self {
309 self.prio = RuleItemPriority::AfterAll;
310 self
311 }
312
313 /// Add another auxiliary identifier to this rule. It can be used to group together multiple
314 /// rules with similar functionality.
315 /// ```
316 /// use markdown_it::common::ruler::Ruler;
317 /// let mut chain = Ruler::<&str, fn (&mut String)>::new();
318 ///
319 /// chain.add("b", |s| s.push_str("B")).alias("BorC");
320 /// chain.add("c", |s| s.push_str("C")).alias("BorC");
321 /// chain.add("a", |s| s.push_str("A")).before("BorC");
322 ///
323 /// let mut result = String::new();
324 /// for f in chain.iter() { f(&mut result); }
325 /// assert_eq!(result, "ABC");
326 /// ```
327 pub fn alias(&mut self, mark: M) -> &mut Self {
328 self.marks.push(mark);
329 self
330 }
331
332 /// Require another rule identified by `mark`, panic if not found.
333 pub fn require(&mut self, mark: M) -> &mut Self {
334 self.cons.push(RuleItemConstraint::Require(mark));
335 self
336 }
337}
338
339#[derive(Debug)]
340enum RuleItemConstraint<M> {
341 Before(M),
342 After(M),
343 Require(M),
344}
345
346#[derive(Debug)]
347enum RuleItemPriority {
348 Normal,
349 BeforeAll,
350 AfterAll,
351}
352
353
354#[cfg(test)]
355mod tests {
356 use super::Ruler;
357
358 #[test]
359 #[should_panic(expected=r#"cyclic dependency: "A" < "B" < "C" < "D" < "E" < "F" < "A""#)]
360 #[cfg(debug_assertions)]
361 fn cyclic_dependency_debug() {
362 let mut r = Ruler::new();
363 r.add("%", ()).after("D");
364 r.add("A", ()).after("B");
365 r.add("E", ()).after("F");
366 r.add("C", ()).after("D");
367 r.add("B", ()).after("C");
368 r.add("D", ()).after("E");
369 r.add("F", ()).after("A");
370 r.compile();
371 }
372
373 #[test]
374 #[should_panic(expected=r#"cyclic dependency"#)]
375 fn cyclic_dependency() {
376 let mut r = Ruler::new();
377 r.add("A", ()).after("B");
378 r.add("B", ()).after("C");
379 r.add("C", ()).after("A");
380 r.compile();
381 }
382
383
384 #[test]
385 #[should_panic(expected=r#"missing dependency: "C" requires "Z"#)]
386 fn missing_require() {
387 let mut r = Ruler::new();
388 r.add("A", ());
389 r.add("B", ()).require("A");
390 r.add("C", ()).require("Z");
391 r.compile();
392 }
393}
crates/markdown-it/src/common/sourcemap.rs created+143
...@@ -0,0 +1,143 @@
1//! Tools to work with source positions and mapping.
2
3#[derive(Debug)]
4/// Holds source code, allows to calculate `line:column` from byte offset.
5pub struct SourceWithLineStarts {
6 src: String,
7 marks: Vec<CharMappingMark>,
8}
9
10impl SourceWithLineStarts {
11 pub fn new(src: &str) -> Self {
12 let mut iterator = src.char_indices().peekable();
13 let mut line = 1;
14 let mut column = 0;
15 let mut marks = vec![CharMappingMark { offset: 0, line, column }];
16
17 loop {
18 match iterator.next() {
19 Some((_, '\r')) if matches!(iterator.peek(), Some((_, '\n'))) => {
20 // ignore \r followed by \n
21 column += 1;
22 }
23 Some((offset, '\r' | '\n')) => {
24 // \r or \n are linebreaks
25 line += 1;
26 column = 0;
27 marks.push(CharMappingMark { offset: offset + 1, line, column });
28 }
29 Some((offset, _)) => {
30 // any other character, just increase position
31 if column % 16 == 0 && column > 0 {
32 marks.push(CharMappingMark { offset, line, column });
33 }
34 column += 1;
35 },
36 None => break,
37 }
38 }
39
40 Self { src: src.to_owned(), marks }
41 }
42
43 fn get_position(&self, byte_offset: usize) -> (u32, u32) {
44 let byte_offset = byte_offset + 1; // include current char
45 let found = match self.marks.binary_search_by(|mark| mark.offset.cmp(&byte_offset)) {
46 Ok(x) => x,
47 Err(x) => x - 1,
48 };
49 let mark = &self.marks[found];
50 let line = mark.line;
51 let mut column = mark.column;
52 for (offset, _) in self.src[mark.offset..].char_indices() {
53 if mark.offset + offset >= byte_offset { break; }
54 column += 1;
55 }
56 (line, column)
57 }
58}
59
60#[derive(Debug)]
61struct CharMappingMark {
62 offset: usize,
63 line: u32,
64 column: u32,
65}
66
67#[derive(Default, Clone, Copy)]
68/// Positions of the start and the end of an AST node.
69pub struct SourcePos {
70 byte_offset: (usize, usize),
71}
72
73impl SourcePos {
74 /// Create positions from byte offsets:
75 /// - start - offset of the first char of the node
76 /// - end - offset of the first char after the node
77 pub fn new(start: usize, end: usize) -> Self {
78 SourcePos {
79 byte_offset: (start, end),
80 }
81 }
82
83 pub fn get_byte_offsets(&self) -> (usize, usize) {
84 self.byte_offset
85 }
86
87 /// Returns (line_start, column_start, line_end, column_end) from given positions
88 pub fn get_positions(&self, map: &SourceWithLineStarts) -> ((u32, u32), (u32, u32)) {
89 let start = map.get_position(self.byte_offset.0);
90 let end_off = if self.byte_offset.1 > 0 { self.byte_offset.1 - 1 } else { self.byte_offset.1 };
91 let end = map.get_position(end_off);
92 (start, end)
93 }
94}
95
96impl std::fmt::Debug for SourcePos {
97 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98 self.byte_offset.fmt(f)
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::SourceWithLineStarts;
105 use super::SourcePos;
106
107 #[test]
108 fn no_linebreaks() {
109 let map = SourceWithLineStarts::new("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM");
110 for i in 0..20 {
111 assert_eq!(SourcePos::new(i, 0).get_positions(&map).0, (1, i as u32 + 1));
112 }
113 }
114
115 #[test]
116 fn unicode() {
117 let map = SourceWithLineStarts::new("!ΑαΒβΓγΔδΕεΖζΗηΘθΙιΚκΛλΜμΝνΞξΟοΠπΡρΣσςΤτΥυΦφΧχΨψΩω");
118 assert_eq!(SourcePos::new(0, 0).get_positions(&map).0, (1, 1));
119 for i in 1..20 {
120 assert_eq!(SourcePos::new(i, 0).get_positions(&map).0, (1, ((i - 1) / 2) as u32 + 2));
121 }
122 }
123
124 #[test]
125 fn many_linebreaks() {
126 let map = SourceWithLineStarts::new("\n\n\n\n\n\n123");
127 for i in 0..6 {
128 assert_eq!(SourcePos::new(i, 0).get_positions(&map).0, (i as u32 + 2, 0));
129 }
130 assert_eq!(SourcePos::new(7, 0).get_positions(&map).0, (7, 2));
131 assert_eq!(SourcePos::new(8, 0).get_positions(&map).0, (7, 3));
132 }
133
134 #[test]
135 fn after_end() {
136 let map = SourceWithLineStarts::new("123");
137 assert_eq!(SourcePos::new(100, 0).get_positions(&map).0, (1, 3));
138 let map = SourceWithLineStarts::new("123\n");
139 assert_eq!(SourcePos::new(100, 0).get_positions(&map).0, (2, 0));
140 let map = SourceWithLineStarts::new("123\n456");
141 assert_eq!(SourcePos::new(100, 0).get_positions(&map).0, (2, 3));
142 }
143}
crates/markdown-it/src/common/typekey.rs created+81
...@@ -0,0 +1,81 @@
1use std::any::{self, TypeId};
2use std::fmt::{self, Debug};
3use std::hash::{Hash, Hasher};
4
5#[readonly::make]
6#[derive(Clone, Copy)]
7/// [std::any::TypeId] and [std::any::type_name] fused into one struct.
8///
9/// It acts as TypeId when hashed or compared, and it acts as type_name when printed.
10/// Used to improve debuggability of type ids in hashmaps in particular.
11/// ```
12/// # use markdown_it::common::TypeKey;
13/// struct A;
14/// struct B;
15///
16/// let mut set = std::collections::HashSet::new();
17///
18/// set.insert(TypeKey::of::<A>());
19/// set.insert(TypeKey::of::<B>());
20///
21/// assert!(set.contains(&TypeKey::of::<A>()));
22/// dbg!(set);
23/// ```
24pub struct TypeKey {
25 /// type id (read only)
26 pub id: TypeId,
27 /// type name (read only)
28 pub name: &'static str,
29}
30
31impl TypeKey {
32 #[must_use]
33 /// Similar to [TypeId::of](std::any::TypeId::of), returns `TypeKey`
34 /// of the type this generic function has been instantiated with.
35 pub fn of<T: ?Sized + 'static>() -> Self {
36 Self { id: TypeId::of::<T>(), name: any::type_name::<T>() }
37 }
38}
39
40impl Hash for TypeKey {
41 fn hash<H: Hasher>(&self, state: &mut H) {
42 self.id.hash(state);
43 }
44}
45
46impl PartialEq for TypeKey {
47 fn eq(&self, other: &Self) -> bool {
48 self.id == other.id
49 }
50}
51
52impl Eq for TypeKey {}
53
54impl Debug for TypeKey {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 write!(f, "{}", self.name)
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use super::TypeKey;
63
64 #[test]
65 fn typekey_eq() {
66 struct A;
67 struct B;
68 assert_eq!(TypeKey { id: std::any::TypeId::of::<A>(), name: "foo" },
69 TypeKey { id: std::any::TypeId::of::<A>(), name: "bar" });
70 assert_ne!(TypeKey { id: std::any::TypeId::of::<A>(), name: "foo" },
71 TypeKey { id: std::any::TypeId::of::<B>(), name: "foo" });
72 }
73
74 #[test]
75 fn typekey_of() {
76 struct A;
77 struct B;
78 assert_eq!(TypeKey::of::<A>(), TypeKey::of::<A>());
79 assert_ne!(TypeKey::of::<A>(), TypeKey::of::<B>());
80 }
81}
crates/markdown-it/src/common/utils.rs created+467
...@@ -0,0 +1,467 @@
1//! Random assortment of functions that's used internally to write plugins.
2
3use entities;
4use once_cell::sync::Lazy;
5use regex::Regex;
6use std::borrow::Cow;
7use std::collections::HashMap;
8
9const UNESCAPE_MD_RE : &str = r##"\\([!"#$%&'()*+,\-./:;<=>?@\[\\\]^_`{|}~])"##;
10const ENTITY_RE : &str = r##"&([A-Za-z#][A-Za-z0-9]{1,31});"##;
11
12static DIGITAL_ENTITY_TEST_RE : Lazy<Regex> = Lazy::new(||
13 Regex::new(r#"(?i)^&#(x[a-f0-9]{1,8}|[0-9]{1,8});$"#).unwrap()
14);
15static UNESCAPE_ALL_RE : Lazy<Regex> = Lazy::new(||
16 Regex::new(&format!("{UNESCAPE_MD_RE}|{ENTITY_RE}")).unwrap()
17);
18
19#[allow(clippy::manual_range_contains)]
20/// Return true if a `code` you got from `&#xHHHH;` entity is a valid charcode.
21///
22/// It returns false for surrogates and non-printables, so it's a subset of `char::from_u32`.
23/// For example, it returns false for 0xFDD0, which is a valid character, but not safe to
24/// render on the screen due to turning you into stone, as per <https://xkcd.com/380/>
25/// ```
26/// # use markdown_it::common::utils::is_valid_entity_code;
27/// assert_eq!(is_valid_entity_code(1), false);
28/// assert_eq!(is_valid_entity_code(32), true);
29/// ```
30pub fn is_valid_entity_code(code: u32) -> bool {
31 // broken sequence
32 if code >= 0xD800 && code <= 0xDFFF { return false; }
33 // never used
34 if code >= 0xFDD0 && code <= 0xFDEF { return false; }
35 if (code & 0xFFFF) == 0xFFFF || (code & 0xFFFF) == 0xFFFE { return false; }
36 // control codes
37 if code <= 0x08 { return false; }
38 if code == 0x0B { return false; }
39 if code >= 0x0E && code <= 0x1F { return false; }
40 if code >= 0x7F && code <= 0x9F { return false; }
41 // out of range
42 if code > 0x10FFFF { return false; }
43 true
44}
45
46/// Check if "&xxxx;" string is a valid HTML entity, return character it represents.
47/// ```
48/// # use markdown_it::common::utils::get_entity_from_str;
49/// assert_eq!(get_entity_from_str("&amp;"), Some("&"));
50/// assert_eq!(get_entity_from_str("&xxx;"), None);
51/// ```
52pub fn get_entity_from_str(str: &str) -> Option<&'static str> {
53 pub static ENTITIES_HASH : Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
54 let mut mapping = HashMap::new();
55 for e in &entities::ENTITIES {
56 if e.entity.ends_with(';') {
57 mapping.insert(e.entity, e.characters);
58 }
59 }
60 mapping
61 });
62
63 ENTITIES_HASH.get(str).copied()
64}
65
66#[allow(clippy::from_str_radix_10)]
67fn replace_entity_pattern(str: &str) -> Option<String> {
68 if let Some(entity) = get_entity_from_str(str) {
69 Some((*entity).to_owned())
70 } else if let Some(captures) = DIGITAL_ENTITY_TEST_RE.captures(str) {
71 let str = captures.get(1).unwrap().as_str();
72 let code = if str.starts_with('x') || str.starts_with('X') {
73 u32::from_str_radix(&str[1..], 16).unwrap()
74 } else {
75 u32::from_str_radix(str, 10).unwrap()
76 };
77
78 if is_valid_entity_code(code) {
79 Some(char::from_u32(code).unwrap().into())
80 } else {
81 None
82 }
83 } else {
84 None
85 }
86}
87
88/// Unescape both entities (`&quot; -> "`) and backslash escapes (`\" -> "`).
89/// ```
90/// # use markdown_it::common::utils::unescape_all;
91/// assert_eq!(unescape_all("&amp;"), "&");
92/// assert_eq!(unescape_all("\\&"), "&");
93/// ```
94pub fn unescape_all(str: &str) -> Cow<str> {
95 if !str.contains('\\') && !str.contains('&') { return Cow::Borrowed(str); }
96
97 UNESCAPE_ALL_RE.replace_all(str, |captures: &regex::Captures| {
98 let s = captures.get(0).unwrap().as_str();
99 if let Some(m) = captures.get(1) {
100 // \" -> "
101 m.as_str().to_owned()
102 } else if let Some(replacement) = replace_entity_pattern(s) {
103 // &quot; -> "
104 replacement
105 } else {
106 s.to_owned()
107 }
108 })
109}
110
111/// Escape `" < > &` with corresponding HTML entities;
112/// ```
113/// # use markdown_it::common::utils::escape_html;
114/// assert_eq!(escape_html("&\""), "&amp;&quot;");
115/// ```
116pub fn escape_html(str: &str) -> Cow<str> {
117 html_escape::encode_double_quoted_attribute(str)
118}
119
120/// Unicode case folding + space normalization, used for for reference labels.
121///
122/// So that strings equal according to commonmark standard are converted to
123/// the same string (lowercase/uppercase differences and spacing go away).
124/// ```
125/// # use markdown_it::common::utils::normalize_reference;
126/// assert_eq!(normalize_reference("hello"), normalize_reference("HELLO"));
127/// assert_eq!(normalize_reference("a b"), normalize_reference("a b"));
128/// ```
129pub fn normalize_reference(str: &str) -> String {
130 static SPACE_RE : Lazy<Regex> = Lazy::new(|| Regex::new(r"\s+").unwrap());
131
132 // Trim and collapse whitespace
133 //
134 let str = SPACE_RE.replace_all(str.trim(), " ");
135
136 // .toLowerCase().toUpperCase() should get rid of all differences
137 // between letter variants.
138 //
139 // Simple .toLowerCase() doesn't normalize 125 code points correctly,
140 // and .toUpperCase doesn't normalize 6 of them (list of exceptions:
141 // İ, ϴ, ẞ, Ω, K, Å - those are already uppercased, but have differently
142 // uppercased versions).
143 //
144 // Here's an example showing how it happens. Lets take greek letter omega:
145 // uppercase U+0398 (Θ), U+03f4 (ϴ) and lowercase U+03b8 (θ), U+03d1 (ϑ)
146 //
147 // Unicode entries:
148 // 0398;GREEK CAPITAL LETTER THETA;Lu;0;L;;;;;N;;;;03B8;
149 // 03B8;GREEK SMALL LETTER THETA;Ll;0;L;;;;;N;;;0398;;0398
150 // 03D1;GREEK THETA SYMBOL;Ll;0;L;<compat> 03B8;;;;N;GREEK SMALL LETTER SCRIPT THETA;;0398;;0398
151 // 03F4;GREEK CAPITAL THETA SYMBOL;Lu;0;L;<compat> 0398;;;;N;;;;03B8;
152 //
153 // Case-insensitive comparison should treat all of them as equivalent.
154 //
155 // But .toLowerCase() doesn't change ϑ (it's already lowercase),
156 // and .toUpperCase() doesn't change ϴ (already uppercase).
157 //
158 // Applying first lower then upper case normalizes any character:
159 // '\u0398\u03f4\u03b8\u03d1'.toLowerCase().toUpperCase() === '\u0398\u0398\u0398\u0398'
160 //
161 // Note: this is equivalent to unicode case folding; unicode normalization
162 // is a different step that is not required here.
163 //
164 // Final result should be uppercased, because it's later stored in an object
165 // (this avoid a conflict with Object.prototype members,
166 // most notably, `__proto__`)
167 //
168 str.to_lowercase().to_uppercase()
169}
170
171/// Count number of characters since last occurrence of `char`.
172///
173/// Finds last occurrence of `char` in `source`, returns number of characters from
174/// that last occurrence. If char is not found, return number of characters total.
175/// ```
176/// # use markdown_it::common::utils::rfind_and_count;
177/// assert_eq!(rfind_and_count("abcde", 'e'), 0);
178/// assert_eq!(rfind_and_count("abcde", 'b'), 3);
179/// assert_eq!(rfind_and_count("abcde", 'z'), 5);
180/// ```
181pub fn rfind_and_count(source: &str, char: char) -> usize {
182 let mut result = 0;
183 for c in source.chars().rev() {
184 if c == char { break; }
185 result += 1;
186 }
187 result
188}
189
190/// Calculate number of spaces from `pos` to first non-space character or EOL.
191///
192/// Tabs are expanded to variable number of spaces with tabstop = 4.
193/// Returns relative indent and offset of first non-space character.
194/// ```
195/// # use markdown_it::common::utils::find_indent_of;
196/// assert_eq!(find_indent_of("\tfoo", 0), (4, 1));
197/// ```
198pub fn find_indent_of(line: &str, mut pos: usize) -> (usize, usize) {
199 let mut chars = line[pos..].chars();
200 let mut indent = 0;
201
202 loop {
203 match chars.next() {
204 Some('\t') => {
205 let bs_count = rfind_and_count(&line[..pos], '\t');
206 indent += 4 - bs_count % 4;
207 pos += 1;
208 }
209 Some(' ') => {
210 indent += 1;
211 pos += 1;
212 }
213 _ => return ( indent, pos ),
214 }
215 }
216}
217
218/// Returns trailing whitespace with total length of `indent`.
219///
220/// Input: a string of characters (presumed whitespaces, can be anything), where each one of
221/// them contributes 1 to indent (except for tabs, whose width may vary with tabstop = 4).
222///
223/// If an indent would split a tab, that tab is replaced with 4 spaces.
224///
225/// Example: cut_right_whitespace_with_tabstops("\t\t", 6) would return " \t" (two preceding
226/// spaces) because first tab gets expanded to 6 spaces.
227/// ```
228/// # use markdown_it::common::utils::cut_right_whitespace_with_tabstops;
229/// assert_eq!(cut_right_whitespace_with_tabstops("\t\t", 6), " \t");
230/// ```
231pub fn cut_right_whitespace_with_tabstops(source: &str, indent: i32) -> Cow<str> {
232 let (num_spaces, start) = calc_right_whitespace_with_tabstops(source, indent);
233
234 if num_spaces > 0 {
235 let mut result = " ".repeat(num_spaces);
236 result += &source[start..];
237 Cow::Owned(result)
238 } else {
239 Cow::Borrowed(&source[start..])
240 }
241}
242
243/// Calculate trailing whitespace with total length of `indent`.
244///
245/// See [cut_right_whitespace_with_tabstops](cut_right_whitespace_with_tabstops)
246/// for algorithm and details.
247///
248/// Returns number of spaces + number of bytes to cut from the end.
249/// ```
250/// # use markdown_it::common::utils::calc_right_whitespace_with_tabstops;
251/// assert_eq!(calc_right_whitespace_with_tabstops("\t\t", 6), (2, 1));
252/// ```
253pub fn calc_right_whitespace_with_tabstops(source: &str, mut indent: i32) -> (usize, usize) {
254 let mut start = source.len();
255 let mut chars = source.char_indices().rev();
256
257 while indent > 0 {
258 match chars.next() {
259 Some((pos, '\t')) => {
260 // previous tab is guaranteed to finish at 0 modulo 4,
261 // so we can finish counting there
262 let indent_from_start = rfind_and_count(&source[..pos], '\t');
263 let tab_width = 4 - indent_from_start as i32 % 4;
264
265 if indent < tab_width {
266 return ( indent as usize, start );
267 }
268
269 indent -= tab_width;
270 start = pos;
271 }
272 Some((pos, _)) => {
273 indent -= 1;
274 start = pos;
275 }
276 None => {
277 start = 0;
278 break;
279 }
280 }
281 }
282
283 ( 0, start )
284}
285
286/// Checks whether a given character should count as punctuation
287///
288/// used to determine word boundaries, made to match the implementation of
289/// `isPunctChar` from the JS library.
290/// This is currently implemented as a `match`, but might be simplified as a
291/// regex if benchmarking shows this to be beneficient.
292pub fn is_punct_char(ch: char) -> bool {
293 use unicode_general_category::get_general_category;
294 use unicode_general_category::GeneralCategory::*;
295
296 match get_general_category(ch) {
297 // P
298 ConnectorPunctuation | DashPunctuation | OpenPunctuation | ClosePunctuation |
299 InitialPunctuation | FinalPunctuation | OtherPunctuation => true,
300
301 // L
302 UppercaseLetter | LowercaseLetter | TitlecaseLetter | ModifierLetter | OtherLetter |
303 // M
304 NonspacingMark | SpacingMark | EnclosingMark |
305 // N
306 DecimalNumber | LetterNumber | OtherNumber |
307 // S
308 MathSymbol | CurrencySymbol | ModifierSymbol | OtherSymbol |
309 // Z
310 SpaceSeparator | LineSeparator | ParagraphSeparator |
311 // C
312 Control | Format | Surrogate | PrivateUse | Unassigned => false
313 }
314}
315
316#[cfg(test)]
317mod tests {
318 use super::cut_right_whitespace_with_tabstops as cut_ws;
319 use super::rfind_and_count;
320 use super::find_indent_of;
321 use super::replace_entity_pattern;
322 use super::unescape_all;
323
324 #[test]
325 fn rfind_and_count_test() {
326 assert_eq!(rfind_and_count("", 'b'), 0);
327 assert_eq!(rfind_and_count("abcde", 'e'), 0);
328 assert_eq!(rfind_and_count("abcde", 'b'), 3);
329 assert_eq!(rfind_and_count("abcde", 'z'), 5);
330 assert_eq!(rfind_and_count("abcεπ", 'b'), 3);
331 }
332
333 #[test]
334 fn find_indent_of_simple_test() {
335 assert_eq!(find_indent_of("a", 0), (0, 0));
336 assert_eq!(find_indent_of(" a", 0), (1, 1));
337 assert_eq!(find_indent_of(" a", 0), (3, 3));
338 assert_eq!(find_indent_of(" ", 0), (4, 4));
339 assert_eq!(find_indent_of("\ta", 0), (4, 1));
340 assert_eq!(find_indent_of(" \ta", 0), (4, 2));
341 assert_eq!(find_indent_of(" \ta", 0), (4, 3));
342 assert_eq!(find_indent_of(" \ta", 0), (4, 4));
343 assert_eq!(find_indent_of(" \ta", 0), (8, 5));
344 }
345
346 #[test]
347 fn find_indent_of_with_offset() {
348 assert_eq!(find_indent_of(" a", 2), (1, 3));
349 assert_eq!(find_indent_of(" a", 2), (2, 4));
350 assert_eq!(find_indent_of(" \ta", 2), (2, 3));
351 assert_eq!(find_indent_of(" \ta", 2), (2, 4));
352 assert_eq!(find_indent_of(" \ta", 2), (6, 5));
353 assert_eq!(find_indent_of(" \ta", 2), (6, 6));
354 }
355
356 #[test]
357 fn find_indent_of_tabs_test() {
358 assert_eq!(find_indent_of(" \t \ta", 1), (7, 5));
359 assert_eq!(find_indent_of(" \t \ta", 2), (6, 5));
360 assert_eq!(find_indent_of(" \t \ta", 3), (4, 5));
361 assert_eq!(find_indent_of(" \t \ta", 4), (3, 5));
362 }
363
364 #[test]
365 fn cut_ws_simple() {
366 assert_eq!(cut_ws("abc", -1), "");
367 assert_eq!(cut_ws("abc", 0), "");
368 assert_eq!(cut_ws("abc", 1), "c");
369 assert_eq!(cut_ws("abc", 2), "bc");
370 assert_eq!(cut_ws("abc", 3), "abc");
371 assert_eq!(cut_ws("abc", 4), "abc");
372 }
373
374 #[test]
375 fn cut_ws_unicode() {
376 assert_eq!(cut_ws("αβγδ", 1), "δ");
377 assert_eq!(cut_ws("αβγδ ", 3), "γδ ");
378 }
379
380 #[test]
381 fn cut_ws_expands_partial_tabs() {
382 assert_eq!(cut_ws("\t", 1), " ");
383 assert_eq!(cut_ws("\t", 2), " ");
384 assert_eq!(cut_ws("\t", 3), " ");
385 assert_eq!(cut_ws("\t\t\t", 5), " \t");
386 assert_eq!(cut_ws("\t\t\t", 7), " \t");
387 }
388
389 #[test]
390 fn cut_ws_retains_full_tabs() {
391 assert_eq!(cut_ws("\t\t\t", 4), "\t");
392 assert_eq!(cut_ws("\t\t\t", 8), "\t\t");
393 }
394
395 #[test]
396 fn cut_ws_proper_tabstops() {
397 assert_eq!(cut_ws("a\t", 1), " ");
398 assert_eq!(cut_ws("a\t", 2), " ");
399 assert_eq!(cut_ws("a\t", 3), "\t");
400 assert_eq!(cut_ws("ab\t", 3), "b\t");
401 assert_eq!(cut_ws("abc\t", 3), "bc\t");
402 }
403
404 #[test]
405 fn cut_ws_proper_tabstops_nested() {
406 assert_eq!(cut_ws("a\tb\t", 2), " ");
407 assert_eq!(cut_ws("a\tb\t", 3), "\t");
408 assert_eq!(cut_ws("a\tb\t", 4), "b\t");
409 assert_eq!(cut_ws("a\tb\t", 5), " b\t");
410 assert_eq!(cut_ws("a\tb\t", 6), " b\t");
411 assert_eq!(cut_ws("a\tb\t", 7), "\tb\t");
412 assert_eq!(cut_ws("a\tb\t", 8), "a\tb\t");
413 }
414
415 #[test]
416 fn cut_ws_different_tabstops_nested() {
417 assert_eq!(cut_ws("abc\tde\tf\tg", 3), " g");
418 assert_eq!(cut_ws("abc\tde\tf\tg", 4), "\tg");
419 assert_eq!(cut_ws("abc\tde\tf\tg", 5), "f\tg");
420 assert_eq!(cut_ws("abc\tde\tf\tg", 6), " f\tg");
421 assert_eq!(cut_ws("abc\tde\tf\tg", 7), "\tf\tg");
422 assert_eq!(cut_ws("abc\tde\tf\tg", 9), "de\tf\tg");
423 assert_eq!(cut_ws("abc\tde\tf\tg", 10), "\tde\tf\tg");
424 }
425
426 #[test]
427 fn test_replace_entity_pattern() {
428 assert_eq!(replace_entity_pattern("&amp;"), Some("&".into()));
429 assert_eq!(replace_entity_pattern("&euro;"), Some("€".into()));
430 assert_eq!(replace_entity_pattern("&#8212;"), Some("—".into()));
431 assert_eq!(replace_entity_pattern("&#x2014;"), Some("—".into()));
432 assert_eq!(replace_entity_pattern("&#X20;"), Some(" ".into()));
433 assert_eq!(replace_entity_pattern("&#x3F;"), Some("?".into()));
434 assert_eq!(replace_entity_pattern("&ffff;"), None);
435 assert_eq!(replace_entity_pattern("&#3F;"), None);
436 assert_eq!(replace_entity_pattern("&#xGG;"), None);
437 }
438
439 #[test]
440 fn test_unescape_all_simple() {
441 assert_eq!(unescape_all("&amp;"), "&");
442 assert_eq!(unescape_all("\\&"), "&");
443 }
444
445 #[test]
446 fn test_unescape_all_xss() {
447 assert_eq!(
448 unescape_all(r#"javascript&#x3A;alert(1)"#),
449 r#"javascript:alert(1)"#);
450
451 assert_eq!(
452 unescape_all(r#"&#74;avascript:alert(1)"#),
453 r#"Javascript:alert(1)"#);
454
455 assert_eq!(
456 unescape_all(r#"&#x26;#74;avascript:alert(1)"#),
457 r#"&#74;avascript:alert(1)"#);
458
459 assert_eq!(
460 unescape_all(r#"\&#74;avascript:alert(1)"#),
461 r#"&#74;avascript:alert(1)"#);
462
463 assert_eq!(
464 unescape_all(r#"&#34;&#62;&#60;script&#62;alert&#40;&#34;xss&#34;&#41;&#60;/script&#62;"#),
465 r#""><script>alert("xss")</script>"#);
466 }
467}
crates/markdown-it/src/examples/ferris/block_rule.rs created+7
...@@ -0,0 +1,7 @@
1//! Replaces `(\/)-------(\/)` with a nice picture.
2//!
3//! ```rust
4//! # const IGNORE : &str = stringify! {
5#![doc=include_str!("../../../examples/ferris/block_rule.rs")]
6//! # };
7//! ```
crates/markdown-it/src/examples/ferris/core_rule.rs created+7
...@@ -0,0 +1,7 @@
1//! Counts the number of crabs lurking around.
2//!
3//! ```rust
4//! # const IGNORE : &str = stringify! {
5#![doc=include_str!("../../../examples/ferris/core_rule.rs")]
6//! # };
7//! ```
crates/markdown-it/src/examples/ferris/inline_rule.rs created+7
...@@ -0,0 +1,7 @@
1//! Replaces `(\/)` with `🦀`.
2//!
3//! ```rust
4//! # const IGNORE : &str = stringify! {
5#![doc=include_str!("../../../examples/ferris/inline_rule.rs")]
6//! # };
7//! ```
crates/markdown-it/src/examples/ferris/mod.rs created+15
...@@ -0,0 +1,15 @@
1#![doc=include_str!("../../../examples/ferris/README.md")]
2//!
3//! ### Implementation
4//!
5//! See [core_rule], [block_rule] and [inline_rule] below for implementations.
6//!
7//! ```rust
8//! # const IGNORE : &str = stringify! {
9#![doc=include_str!("../../../examples/ferris/main.rs")]
10//! # };
11//! ```
12
13pub mod block_rule;
14pub mod core_rule;
15pub mod inline_rule;
crates/markdown-it/src/examples/mod.rs created+4
...@@ -0,0 +1,4 @@
1//! Copied content from `examples/` folder (docs only).
2
3pub mod ferris;
4mod testreadme;
crates/markdown-it/src/examples/testreadme.rs created+2
...@@ -0,0 +1,2 @@
1// This file is used only to test examples in README.
2#![doc=include_str!("../../README.md")]
crates/markdown-it/src/generics/inline/code_pair.rs created+142
...@@ -0,0 +1,142 @@
1//! Structure similar to `` `code span` `` with configurable markers of variable length.
2//!
3//! It allows you to define a custom structure with variable number of markers
4//! (e.g. with `%` defined as a marker, user can write `%foo%` or `%%%foo%%%`
5//! resulting in the same node).
6//!
7//! You add a custom structure by using [add_with] function, which takes following arguments:
8//! - `MARKER` - marker character
9//! - `md` - parser instance
10//! - `f` - function that should return your custom [Node]
11//!
12//! Here is an example of a rule turning `%foo%` into `🦀foo🦀`:
13//!
14//! ```rust
15//! use markdown_it::generics::inline::code_pair;
16//! use markdown_it::{MarkdownIt, Node, NodeValue, Renderer};
17//!
18//! #[derive(Debug)]
19//! struct Ferris;
20//! impl NodeValue for Ferris {
21//! fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
22//! fmt.text("🦀");
23//! fmt.contents(&node.children);
24//! fmt.text("🦀");
25//! }
26//! }
27//!
28//! let md = &mut MarkdownIt::new();
29//! code_pair::add_with::<'%'>(md, |_| Node::new(Ferris));
30//! let html = md.parse("hello %world%").render();
31//! assert_eq!(html.trim(), "hello 🦀world🦀");
32//! ```
33//!
34//! This generic structure follows exact rules of code span in CommonMark:
35//!
36//! 1. Literal marker character sequence can be used inside of structure if its length
37//! doesn't match length of the opening/closing sequence (e.g. with `%` defined
38//! as a marker, `%%foo%bar%%` gets parsed as `Node("foo%bar")`).
39//!
40//! 2. Single space inside is trimmed to allow you to write `% %%foo %` to be parsed as
41//! `Node("%%foo")`.
42//!
43//! If you define two structures with the same marker, only the first one will work.
44//!
45use crate::parser::extset::{InlineRootExt, MarkdownItExt};
46use crate::parser::inline::{InlineRule, InlineState, Text};
47use crate::{MarkdownIt, Node};
48
49#[derive(Debug, Default)]
50struct CodePairCache<const MARKER: char> {
51 scanned: bool,
52 max: Vec<usize>,
53}
54impl<const MARKER: char> InlineRootExt for CodePairCache<MARKER> {}
55
56#[derive(Debug)]
57struct CodePairConfig<const MARKER: char>(fn (usize) -> Node);
58impl<const MARKER: char> MarkdownItExt for CodePairConfig<MARKER> {}
59
60pub fn add_with<const MARKER: char>(md: &mut MarkdownIt, f: fn (length: usize) -> Node) {
61 md.ext.insert(CodePairConfig::<MARKER>(f));
62
63 md.inline.add_rule::<CodePairScanner<MARKER>>();
64}
65
66#[doc(hidden)]
67pub struct CodePairScanner<const MARKER: char>;
68impl<const MARKER: char> InlineRule for CodePairScanner<MARKER> {
69 const MARKER: char = MARKER;
70
71 fn run(state: &mut InlineState) -> Option<(Node, usize)> {
72 let mut chars = state.src[state.pos..state.pos_max].chars();
73 if chars.next().unwrap() != MARKER { return None; }
74 if state.trailing_text_get().ends_with(MARKER) { return None; }
75
76 let mut pos = state.pos + 1;
77
78 // scan marker length
79 while Some(MARKER) == chars.next() {
80 pos += 1;
81 }
82
83 // backtick length => last seen position
84 let backticks = state.inline_ext.get_or_insert_default::<CodePairCache<MARKER>>();
85 let opener_len = pos - state.pos;
86
87 if backticks.scanned && backticks.max.get(opener_len).copied().unwrap_or(0) <= state.pos {
88 // performance note: adding entire sequence into pending is 5x faster,
89 // but it will interfere with other rules working on the same char;
90 // and it is extremely rare that user would put a thousand "`" in text
91 return None;
92 }
93
94 let mut match_start;
95 let mut match_end = pos;
96
97 // Nothing found in the cache, scan until the end of the line (or until marker is found)
98 while let Some(p) = state.src[match_end..state.pos_max].find(MARKER) {
99 match_start = match_end + p;
100
101 // scan marker length
102 match_end = match_start + 1;
103 chars = state.src[match_end..state.pos_max].chars();
104
105 while Some(MARKER) == chars.next() {
106 match_end += 1;
107 }
108
109 let closer_len = match_end - match_start;
110
111 if closer_len == opener_len {
112 // Found matching closer length.
113 let mut content = state.src[pos..match_start].to_owned().replace('\n', " ");
114 if content.starts_with(' ') && content.ends_with(' ') && content.len() > 2 {
115 content[1..content.len() - 1].to_owned().clone_into(&mut content);
116 pos += 1;
117 match_start -= 1;
118 }
119
120 let f = state.md.ext.get::<CodePairConfig<MARKER>>().unwrap().0;
121 let mut node = f(opener_len);
122
123 let mut inner_node = Node::new(Text { content });
124 inner_node.srcmap = state.get_map(pos, match_start);
125 node.children.push(inner_node);
126
127 return Some((node, match_end - state.pos));
128 }
129
130 // Some different length found, put it in cache as upper limit of where closer can be found
131 let backticks = state.inline_ext.get_mut::<CodePairCache<MARKER>>().unwrap();
132 while backticks.max.len() <= closer_len { backticks.max.push(0); }
133 backticks.max[closer_len] = match_start;
134 }
135
136 // Scanned through the end, didn't find anything
137 let backticks = state.inline_ext.get_mut::<CodePairCache<MARKER>>().unwrap();
138 backticks.scanned = true;
139
140 None
141 }
142}
crates/markdown-it/src/generics/inline/emph_pair.rs created+390
...@@ -0,0 +1,390 @@
1//! Structure similar to `*emphasis*` with configurable markers of fixed length.
2//!
3//! There are many structures in various markdown flavors that
4//! can be implemented with this, namely:
5//!
6//! - `*emphasis*` or `_emphasis_` -> `<em>emphasis</em>`
7//! - `**strong**` or `__strong__` -> `<strong>strong</strong>`
8//! - `~~strikethrough~~` -> `<s>strikethrough</s>`
9//! - `==marked==` -> `<mark>marked</mark>`
10//! - `++inserted++` -> `<ins>inserted</ins>`
11//! - `~subscript~` -> `<sub>subscript</sub>`
12//! - `^superscript^` -> `<sup>superscript</sup>`
13//!
14//! You add a custom structure by using [add_with] function, which takes following arguments:
15//! - `MARKER` - marker character
16//! - `LENGTH` - length of the opening/closing marker (can be 1, 2 or 3)
17//! - `CAN_SPLIT_WORD` - whether this structure can be found in the middle of the word
18//! (for example, note the difference between `foo*bar*baz` and `foo_bar_baz`
19//! in CommonMark - first one is an emphasis, second one isn't)
20//! - `md` - parser instance
21//! - `f` - function that should return your custom [Node]
22//!
23//! Here is an example of implementing superscript in your custom code:
24//!
25//! ```rust
26//! use markdown_it::generics::inline::emph_pair;
27//! use markdown_it::{MarkdownIt, Node, NodeValue, Renderer};
28//!
29//! #[derive(Debug)]
30//! struct Superscript;
31//! impl NodeValue for Superscript {
32//! fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
33//! fmt.open("sup", &node.attrs);
34//! fmt.contents(&node.children);
35//! fmt.close("sup");
36//! }
37//! }
38//!
39//! let md = &mut MarkdownIt::new();
40//! emph_pair::add_with::<'^', 1, true>(md, || Node::new(Superscript));
41//!
42//! let html = md.parse("e^iπ^+1=0").render();
43//! assert_eq!(html.trim(), "e<sup>iπ</sup>+1=0");
44//! ```
45//!
46//! Note that these structures have lower priority than the rest of the rules,
47//! e.g. `` *foo`bar*baz` `` is parsed as `*foo<code>bar*baz</code>`.
48//!
49use std::cmp::min;
50
51use crate::common::sourcemap::SourcePos;
52use crate::parser::core::CoreRule;
53use crate::parser::extset::{MarkdownItExt, NodeExt};
54use crate::parser::inline::builtin::InlineParserRule;
55use crate::parser::inline::{InlineRule, InlineState, Text};
56use crate::{MarkdownIt, Node, NodeValue};
57
58#[derive(Debug, Default)]
59struct PairConfig<const MARKER: char> {
60 inserted: bool,
61 fns: [Option<fn() -> Node>; 3],
62}
63impl<const MARKER: char> MarkdownItExt for PairConfig<MARKER> {}
64
65#[derive(Debug, Default)]
66struct OpenersBottom<const MARKER: char>([usize; 6]);
67impl<const MARKER: char> NodeExt for OpenersBottom<MARKER> {}
68
69#[derive(Debug, Clone)]
70#[doc(hidden)]
71pub struct EmphMarker {
72 // Starting marker
73 pub marker: char,
74
75 // Total length of these series of delimiters.
76 pub length: usize,
77
78 // Remaining length that's not already matched to other delimiters.
79 pub remaining: usize,
80
81 // Boolean flags that determine if this delimiter could open or close
82 // an emphasis.
83 pub open: bool,
84 pub close: bool,
85
86 // Inline position (offset into the stripped inline text) where this
87 // delimiter starts. Stored so that `run` can compute the correct
88 // inline-coordinate token length when the delimiter spans multiple
89 // block-quote continuation lines (where srcmap offsets include the
90 // stripped `> ` bytes and therefore exceed the inline position).
91 pub inline_pos: usize,
92}
93
94// this node is supposed to be replaced by actual emph or text node
95impl NodeValue for EmphMarker {}
96
97pub fn add_with<const MARKER: char, const LENGTH: u8, const CAN_SPLIT_WORD: bool>(
98 md: &mut MarkdownIt,
99 f: fn() -> Node,
100) {
101 let pair_config = md.ext.get_or_insert_default::<PairConfig<MARKER>>();
102 pair_config.fns[LENGTH as usize - 1] = Some(f);
103
104 if !pair_config.inserted {
105 pair_config.inserted = true;
106 md.inline
107 .add_rule::<EmphPairScanner<MARKER, CAN_SPLIT_WORD>>();
108 }
109
110 if !md.has_rule::<FragmentsJoin>() {
111 md.add_rule::<FragmentsJoin>()
112 .before_all()
113 .after::<InlineParserRule>();
114 }
115}
116
117#[doc(hidden)]
118pub struct EmphPairScanner<const MARKER: char, const CAN_SPLIT_WORD: bool>;
119impl<const MARKER: char, const CAN_SPLIT_WORD: bool> InlineRule
120 for EmphPairScanner<MARKER, CAN_SPLIT_WORD>
121{
122 const MARKER: char = MARKER;
123
124 // this rule works on a closing marker, so for technical reasons any rules trying to skip it
125 // should see just plain text
126 fn check(_: &mut InlineState) -> Option<usize> {
127 None
128 }
129
130 fn run(state: &mut InlineState) -> Option<(Node, usize)> {
131 let mut chars = state.src[state.pos..state.pos_max].chars();
132 if chars.next().unwrap() != MARKER {
133 return None;
134 }
135
136 let scanned = state.scan_delims(state.pos, CAN_SPLIT_WORD);
137 let inline_closer_start = state.pos;
138 let mut node = Node::new(EmphMarker {
139 marker: MARKER,
140 length: scanned.length,
141 remaining: scanned.length,
142 open: scanned.can_open,
143 close: scanned.can_close,
144 inline_pos: state.pos,
145 });
146 node.srcmap = state.get_map(state.pos, state.pos + scanned.length);
147 let (node, opener_inline_pos) = scan_and_match_delimiters::<MARKER>(state, node);
148 // backtrack to keep correct source maps
149 state.pos += scanned.length;
150 // Compute token_len in inline coordinates. Using srcmap byte offsets
151 // (map.1 - map.0) is incorrect when the token spans multiple blockquote
152 // continuation lines because the srcmap reflects the original file bytes
153 // (which include the stripped `> ` prefixes) rather than the shorter
154 // inline text. When a match was found we use the opener's stored inline
155 // position; when no match was found the token is only the scanned
156 // delimiter itself.
157 let token_len = match opener_inline_pos {
158 Some(oip) => state.pos - oip,
159 None => scanned.length,
160 };
161 // Sanity: keep state.pos valid (should never underflow, but guard anyway)
162 debug_assert!(
163 state.pos >= token_len,
164 "emph backtrack underflow: pos={} token_len={} closer_start={}",
165 state.pos,
166 token_len,
167 inline_closer_start
168 );
169 state.pos -= token_len;
170 Some((node, token_len))
171 }
172}
173
174/// Assuming last token is a closing delimiter we just inserted,
175/// try to find opener(s). If any are found, move stuff to nested emph node.
176///
177/// Returns `(node, opener_inline_pos)`. `opener_inline_pos` is `Some` when a
178/// match was found; the value is the `inline_pos` field of the matched opener.
179fn scan_and_match_delimiters<const MARKER: char>(
180 state: &mut InlineState,
181 mut closer_token: Node,
182) -> (Node, Option<usize>) {
183 if state.node.children.is_empty() {
184 return (closer_token, None);
185 } // must have at least opener and closer
186
187 let mut closer = closer_token.cast_mut::<EmphMarker>().unwrap().clone();
188 if !closer.close {
189 return (closer_token, None);
190 }
191
192 // Previously calculated lower bounds (previous fails)
193 // for each marker, each delimiter length modulo 3,
194 // and for whether this closer can be an opener;
195 // https://github.com/commonmark/cmark/commit/34250e12ccebdc6372b8b49c44fab57c72443460
196 let openers_for_marker = state
197 .node
198 .ext
199 .get_or_insert_default::<OpenersBottom<MARKER>>();
200 let openers_parameter = (closer.open as usize) * 3 + closer.length % 3;
201
202 let min_opener_idx = openers_for_marker.0[openers_parameter];
203
204 let mut idx = state.node.children.len() - 1;
205 let mut new_min_opener_idx = idx;
206 let mut matched_opener_inline_pos: Option<usize> = None;
207 while idx > min_opener_idx {
208 idx -= 1;
209
210 let Some(opener) = state.node.children[idx].cast::<EmphMarker>() else {
211 continue;
212 };
213
214 let mut opener = opener.clone();
215 if opener.open && opener.marker == closer.marker && !is_odd_match(&opener, &closer) {
216 while closer.remaining > 0 && opener.remaining > 0 {
217 let max_marker_len = min(3, min(opener.remaining, closer.remaining));
218 let mut matched_rule = None;
219 let fns = &state.md.ext.get::<PairConfig<MARKER>>().unwrap().fns;
220 for marker_len in (1..=max_marker_len).rev() {
221 if let Some(f) = fns[marker_len - 1] {
222 matched_rule = Some((marker_len, f));
223 break;
224 }
225 }
226
227 // If matched_fn isn't found, it can only mean that function is defined for larger marker
228 // than we have (e.g. function defined for **, we have *).
229 // Treat this as "marker not found".
230 if matched_rule.is_none() {
231 break;
232 }
233
234 let (marker_len, marker_fn) = matched_rule.unwrap();
235
236 closer.remaining -= marker_len;
237 opener.remaining -= marker_len;
238
239 let mut new_token = marker_fn();
240 new_token.children = state.node.children.split_off(idx + 1);
241
242 // cut marker_len chars from start, i.e. "12345" -> "345"
243 let mut end_map_pos = 0;
244 if let Some(map) = closer_token.srcmap {
245 let (start, end) = map.get_byte_offsets();
246 closer_token.srcmap = Some(SourcePos::new(start + marker_len, end));
247 end_map_pos = start + marker_len;
248 }
249
250 // cut marker_len chars from end, i.e. "12345" -> "123"
251 let mut start_map_pos = 0;
252 let opener_token = state.node.children.last_mut().unwrap();
253 if let Some(map) = opener_token.srcmap {
254 let (start, end) = map.get_byte_offsets();
255 opener_token.srcmap = Some(SourcePos::new(start, end - marker_len));
256 start_map_pos = end - marker_len;
257 }
258
259 new_token.srcmap = state.get_map(start_map_pos, end_map_pos);
260
261 // remove empty node as a small optimization so we can do less work later
262 if opener.remaining == 0 {
263 state.node.children.pop();
264 }
265
266 new_min_opener_idx = 0;
267 if matched_opener_inline_pos.is_none() {
268 matched_opener_inline_pos = Some(opener.inline_pos);
269 }
270 state.node.children.push(new_token);
271 }
272 }
273
274 if opener.remaining > 0 {
275 state.node.children[idx].replace(opener);
276 } // otherwise node was already deleted
277 }
278
279 if new_min_opener_idx != 0 {
280 // If match for this delimiter run failed, we want to set lower bound for
281 // future lookups. This is required to make sure algorithm has linear
282 // complexity.
283 //
284 // See details here:
285 // https://github.com/commonmark/cmark/issues/178#issuecomment-270417442
286 //
287 let openers_for_marker = state
288 .node
289 .ext
290 .get_or_insert_default::<OpenersBottom<MARKER>>();
291 openers_for_marker.0[openers_parameter] = new_min_opener_idx;
292 }
293
294 // remove empty node as a small optimization so we can do less work later
295 if closer.remaining > 0 {
296 closer_token.replace(closer);
297 (closer_token, matched_opener_inline_pos)
298 } else {
299 (
300 state.node.children.pop().unwrap(),
301 matched_opener_inline_pos,
302 )
303 }
304}
305
306fn is_odd_match(opener: &EmphMarker, closer: &EmphMarker) -> bool {
307 // from spec:
308 //
309 // If one of the delimiters can both open and close emphasis, then the
310 // sum of the lengths of the delimiter runs containing the opening and
311 // closing delimiters must not be a multiple of 3 unless both lengths
312 // are multiples of 3.
313 //
314 #[allow(clippy::collapsible_if)]
315 if opener.close || closer.open {
316 if (opener.length + closer.length) % 3 == 0 {
317 if opener.length % 3 != 0 || closer.length % 3 != 0 {
318 return true;
319 }
320 }
321 }
322
323 false
324}
325
326#[doc(hidden)]
327pub struct FragmentsJoin;
328impl CoreRule for FragmentsJoin {
329 fn run(node: &mut Node, _: &MarkdownIt) {
330 node.walk_mut(|node, _| fragments_join(node));
331 }
332}
333
334/// Clean up tokens after emphasis and strikethrough postprocessing:
335/// merge adjacent text nodes into one and re-calculate all token levels
336///
337/// This is necessary because initially emphasis delimiter markers (*, _, ~)
338/// are treated as their own separate text tokens. Then emphasis rule either
339/// leaves them as text (needed to merge with adjacent text) or turns them
340/// into opening/closing tags (which messes up levels inside).
341///
342fn fragments_join(node: &mut Node) {
343 // replace all emph markers with text tokens
344 for token in node.children.iter_mut() {
345 if let Some(data) = token.cast::<EmphMarker>() {
346 let content = data.marker.to_string().repeat(data.remaining);
347 token.replace(Text { content });
348 }
349 }
350
351 // collapse adjacent text tokens
352 for idx in 1..node.children.len() {
353 let (tokens1, tokens2) = node.children.split_at_mut(idx);
354
355 let token1 = tokens1.last_mut().unwrap();
356 let Some(t1_data) = token1.cast_mut::<Text>() else {
357 continue;
358 };
359
360 let token2 = tokens2.first_mut().unwrap();
361 let Some(t2_data) = token2.cast_mut::<Text>() else {
362 continue;
363 };
364
365 // concat contents
366 let t2_content = std::mem::take(&mut t2_data.content);
367 t1_data.content += &t2_content;
368
369 // adjust source maps
370 if let Some(map1) = token1.srcmap {
371 if let Some(map2) = token2.srcmap {
372 token1.srcmap = Some(SourcePos::new(
373 map1.get_byte_offsets().0,
374 map2.get_byte_offsets().1,
375 ));
376 }
377 }
378
379 node.children.swap(idx - 1, idx);
380 }
381
382 // remove all empty tokens
383 node.children.retain(|token| {
384 if let Some(data) = token.cast::<Text>() {
385 !data.content.is_empty()
386 } else {
387 true
388 }
389 });
390}
crates/markdown-it/src/generics/inline/full_link.rs created+440
...@@ -0,0 +1,440 @@
1//! Structure similar to `[link](<to> "stuff")` with configurable prefix.
2//!
3//! There are two structures in CommonMark that match this syntax:
4//! - links - `[text](<href> "title")`
5//! - images - `![alt](<src> "title")`
6//!
7//! You can add custom rules like `~[foo](<bar> "baz")`. Let us know if
8//! you come up with fun use case to add as an example!
9//!
10//! Add a custom structure by using [add_prefix] function, which takes following arguments:
11//! - `PREFIX` - marker character before label (`!` in case of images)
12//! - `ENABLE_NESTED` - allow nested links inside
13//! - `md` - parser instance
14//! - `f` - function that should return your custom [Node] given href and title
15//!
16use std::collections::HashMap;
17
18use crate::common::utils::unescape_all;
19use crate::parser::extset::{InlineRootExt, MarkdownItExt};
20use crate::parser::inline::{InlineRule, InlineState};
21use crate::plugins::cmark::block::reference::ReferenceMap;
22use crate::{MarkdownIt, Node};
23
24#[derive(Debug)]
25struct LinkCfg<const PREFIX: char>(fn (Option<String>, Option<String>) -> Node);
26impl<const PREFIX: char> MarkdownItExt for LinkCfg<PREFIX> {}
27
28/// adds custom rule with no prefix
29pub fn add<const ENABLE_NESTED: bool>(
30 md: &mut MarkdownIt,
31 f: fn (url: Option<String>, title: Option<String>) -> Node
32) {
33 md.ext.insert(LinkCfg::<'\0'>(f));
34 md.inline.add_rule::<LinkScanner<ENABLE_NESTED>>();
35 if !md.inline.has_rule::<LinkScannerEnd>() {
36 md.inline.add_rule::<LinkScannerEnd>();
37 }
38}
39
40/// adds custom rule with given `PREFIX` character
41pub fn add_prefix<const PREFIX: char, const ENABLE_NESTED: bool>(
42 md: &mut MarkdownIt,
43 f: fn (url: Option<String>, title: Option<String>) -> Node
44) {
45 md.ext.insert(LinkCfg::<PREFIX>(f));
46 md.inline.add_rule::<LinkPrefixScanner<PREFIX, ENABLE_NESTED>>();
47 if !md.inline.has_rule::<LinkScannerEnd>() {
48 md.inline.add_rule::<LinkScannerEnd>();
49 }
50}
51
52#[doc(hidden)]
53pub struct LinkScanner<const ENABLE_NESTED: bool>;
54impl<const ENABLE_NESTED: bool> InlineRule for LinkScanner<ENABLE_NESTED> {
55 const MARKER: char = '[';
56
57 fn check(state: &mut InlineState) -> Option<usize> {
58 let mut chars = state.src[state.pos..state.pos_max].chars();
59 if chars.next().unwrap() != '[' { return None; }
60 rule_check(state, ENABLE_NESTED, 0)
61 }
62
63 fn run(state: &mut InlineState) -> Option<(Node, usize)> {
64 let mut chars = state.src[state.pos..state.pos_max].chars();
65 if chars.next().unwrap() != '[' { return None; }
66 let f = state.md.ext.get::<LinkCfg<'\0'>>().unwrap().0;
67 rule_run(state, ENABLE_NESTED, 0, f)
68 }
69}
70
71#[doc(hidden)]
72pub struct LinkPrefixScanner<const PREFIX: char, const ENABLE_NESTED: bool>;
73impl<const PREFIX: char, const ENABLE_NESTED: bool> InlineRule for LinkPrefixScanner<PREFIX, ENABLE_NESTED> {
74 const MARKER: char = PREFIX;
75
76 fn check(state: &mut InlineState) -> Option<usize> {
77 let mut chars = state.src[state.pos..state.pos_max].chars();
78 if chars.next() != Some(PREFIX) { return None; }
79 if chars.next() != Some('[') { return None; }
80 rule_check(state, ENABLE_NESTED, 1)
81 }
82
83 fn run(state: &mut InlineState) -> Option<(Node, usize)> {
84 let mut chars = state.src[state.pos..state.pos_max].chars();
85 if chars.next() != Some(PREFIX) { return None; }
86 if chars.next() != Some('[') { return None; }
87 let f = state.md.ext.get::<LinkCfg<PREFIX>>().unwrap().0;
88 rule_run(state, ENABLE_NESTED, 1, f)
89 }
90}
91
92#[doc(hidden)]
93/// this rule makes sure that parser is stopped on "]" character,
94/// but it actually doesn't do anything
95pub struct LinkScannerEnd;
96impl InlineRule for LinkScannerEnd {
97 const MARKER: char = ']';
98
99 fn check(_: &mut InlineState) -> Option<usize> { None }
100 fn run(_: &mut InlineState) -> Option<(Node, usize)> { None }
101}
102
103fn rule_check(state: &mut InlineState, enable_nested: bool, offset: usize) -> Option<usize> {
104 if let Some(result) = parse_link(state, state.pos + offset, enable_nested) {
105 Some(result.end - state.pos)
106 } else {
107 None
108 }
109}
110
111fn rule_run(
112 state: &mut InlineState,
113 enable_nested: bool,
114 offset: usize,
115 f: fn (Option<String>, Option<String>) -> Node
116) -> Option<(Node, usize)> {
117 let start = state.pos;
118 let result = parse_link(state, state.pos + offset, enable_nested)?;
119
120 //
121 // We found the end of the link, and know for a fact it's a valid link;
122 // so all that's left to do is to call tokenizer.
123 //
124 let old_node = std::mem::replace(&mut state.node, f(result.href, result.title));
125 let max = state.pos_max;
126
127 state.link_level += 1;
128 state.pos = result.label_start;
129 state.pos_max = result.label_end;
130 state.md.inline.tokenize(state);
131 state.pos = start;
132 state.pos_max = max;
133 state.link_level -= 1;
134
135 let node = std::mem::replace(&mut state.node, old_node);
136 Some((node, result.end - state.pos))
137}
138
139#[derive(Debug, Default)]
140struct LinkLabelScanCache(HashMap<(usize, bool), Option<usize>>);
141impl InlineRootExt for LinkLabelScanCache {}
142
143
144// Parse link label
145//
146// this function assumes that first character ("[") already matches;
147// returns the end of the label
148fn parse_link_label(state: &mut InlineState, start: usize, enable_nested: bool) -> Option<usize> {
149 let cache = state.inline_ext.get_or_insert_default::<LinkLabelScanCache>();
150 if let Some(&cached) = cache.0.get(&(start, enable_nested)) {
151 return cached;
152 }
153
154 let old_pos = state.pos;
155 let mut found = false;
156 let mut label_end = None;
157 let mut level = 1;
158
159 state.pos = start + 1;
160
161 while let Some(ch) = state.src[state.pos..state.pos_max].chars().next() {
162 if ch == ']' {
163 level -= 1;
164 if level == 0 {
165 found = true;
166 break;
167 }
168 }
169
170 let prev_pos = state.pos;
171 state.md.inline.skip_token(state);
172 if ch == '[' {
173 if prev_pos == state.pos - 1 {
174 // increase level if we find text `[`, which is not a part of any token
175 level += 1;
176
177 let cache = state.inline_ext.get_or_insert_default::<LinkLabelScanCache>();
178 if let Some(&cached) = cache.0.get(&(prev_pos, enable_nested)) {
179 // maybe cache appeared as a result of skip_token
180 if let Some(cached_pos) = cached {
181 state.pos = cached_pos;
182 } else {
183 break;
184 }
185 }
186
187 } else if !enable_nested {
188 break;
189 }
190 }
191 }
192
193 if found {
194 label_end = Some(state.pos);
195 }
196
197 // restore old state
198 state.pos = old_pos;
199
200 let cache = state.inline_ext.get_or_insert_default::<LinkLabelScanCache>();
201 cache.0.insert((start, enable_nested), label_end);
202
203 label_end
204}
205
206
207pub struct ParseLinkFragmentResult {
208 /// end position
209 pub pos: usize,
210 /// number of linebreaks inside
211 pub lines: usize,
212 /// parsed result
213 pub str: String,
214}
215
216
217/// Helper function used to parse `<href>` part of the links with optional brackets.
218pub fn parse_link_destination(str: &str, start: usize, max: usize) -> Option<ParseLinkFragmentResult> {
219 let mut chars = str[start..max].chars().peekable();
220 let mut pos = start;
221
222 if let Some('<') = chars.peek() {
223 chars.next(); // skip '<'
224 pos += 1;
225 loop {
226 match chars.next() {
227 Some('\n' | '<') | None => return None,
228 Some('>') => {
229 return Some(ParseLinkFragmentResult {
230 pos: pos + 1,
231 lines: 0,
232 str: unescape_all(&str[start + 1..pos]).into_owned(),
233 });
234 }
235 Some('\\') => {
236 match chars.next() {
237 None => return None,
238 Some(x) => pos += 1 + x.len_utf8(),
239 }
240 }
241 Some(x) => {
242 pos += x.len_utf8();
243 }
244 }
245 }
246 } else {
247 let mut level : u32 = 0;
248 loop {
249 match chars.next() {
250 // space + ascii control characters
251 Some('\0'..=' ' | '\x7f') | None => break,
252 Some('\\') => {
253 match chars.next() {
254 Some(' ') | None => break,
255 Some(x) => pos += 1 + x.len_utf8(),
256 }
257 }
258 Some('(') => {
259 level += 1;
260 if level > 32 { return None; }
261 pos += 1;
262 }
263 Some(')') => {
264 if level == 0 { break; }
265 level -= 1;
266 pos += 1;
267 }
268 Some(x) => {
269 pos += x.len_utf8();
270 }
271 }
272 }
273
274 if level != 0 { return None; }
275
276 Some(ParseLinkFragmentResult {
277 pos,
278 lines: 0,
279 str: unescape_all(&str[start..pos]).into_owned(),
280 })
281 }
282}
283
284
285/// Helper function used to parse `"title"` part of the links (with `'title'` or `(title)` alternative syntax).
286pub fn parse_link_title(str: &str, start: usize, max: usize) -> Option<ParseLinkFragmentResult> {
287 let mut chars = str[start..max].chars();
288 let mut pos = start + 1;
289 let mut lines = 0;
290
291 let marker = match chars.next() {
292 Some('"') => '"',
293 Some('\'') => '\'',
294 Some('(') => ')',
295 None | Some(_) => return None,
296 };
297
298 loop {
299 match chars.next() {
300 Some(ch) if ch == marker => {
301 return Some(ParseLinkFragmentResult {
302 pos: pos + 1,
303 lines,
304 str: unescape_all(&str[start + 1..pos]).into_owned(),
305 });
306 }
307 Some('(') if marker == ')' => {
308 return None;
309 }
310 Some('\n') => {
311 pos += 1;
312 lines += 1;
313 }
314 Some('\\') => {
315 match chars.next() {
316 None => return None,
317 Some(x) => pos += 1 + x.len_utf8(),
318 }
319 }
320 Some(x) => {
321 pos += x.len_utf8();
322 }
323 None => {
324 return None;
325 }
326 }
327 }
328}
329
330struct ParseLinkResult {
331 pub label_start: usize,
332 pub label_end: usize,
333 pub href: Option<String>,
334 pub title: Option<String>,
335 pub end: usize,
336}
337
338// Parses [link](<to> "stuff")
339//
340// this function assumes that first character ("[") already matches
341//
342fn parse_link(state: &mut InlineState, pos: usize, enable_nested: bool) -> Option<ParseLinkResult> {
343 let label_end = parse_link_label(state, pos, enable_nested)?;
344 let label_start = pos + 1;
345 let mut pos = label_end + 1;
346 let mut chars = state.src[pos..state.pos_max].chars();
347 let mut href = None;
348 let mut title = None;
349
350 if let Some('(') = chars.next() {
351 //
352 // Inline link
353 //
354
355 // [link]( <href> "title" )
356 // ^^ skipping these spaces
357 pos += 1;
358 while let Some(' ' | '\t' | '\n') = chars.next() {
359 pos += 1;
360 }
361
362 // [link]( <href> "title" )
363 // ^^^^^^ parsing link destination
364 if let Some(res) = parse_link_destination(&state.src, pos, state.pos_max) {
365 let href_candidate = state.md.link_formatter.normalize_link(&res.str);
366 if state.md.link_formatter.validate_link(&href_candidate).is_some() {
367 pos = res.pos;
368 href = Some(href_candidate);
369 }
370
371 // [link]( <href> "title" )
372 // ^^ skipping these spaces
373 let mut chars = state.src[pos..state.pos_max].chars();
374 while let Some(' ' | '\t' | '\n') = chars.next() {
375 pos += 1;
376 }
377
378 if let Some(res) = parse_link_title(&state.src, pos, state.pos_max) {
379 title = Some(res.str);
380 pos = res.pos;
381
382 // [link]( <href> "title" )
383 // ^^ skipping these spaces
384 let mut chars = state.src[pos..state.pos_max].chars();
385 while let Some(' ' | '\t' | '\n') = chars.next() {
386 pos += 1;
387 }
388 }
389 }
390
391 if let Some(')') = state.src[pos..state.pos_max].chars().next() {
392 return Some(ParseLinkResult {
393 label_start,
394 label_end,
395 href,
396 title,
397 end: pos + 1,
398 })
399 }
400 }
401
402 //
403 // Link reference
404 //
405 // TODO: check if I have any references?
406 pos = label_end + 1;
407 let mut maybe_label = None;
408
409 match state.src[pos..state.pos_max].chars().next() {
410 Some('[') => {
411 if let Some(x) = parse_link_label(state, pos, false) {
412 maybe_label = Some(&state.src[pos + 1..x]);
413 pos = x + 1;
414 } else {
415 pos = label_end + 1;
416 }
417 }
418 _ => pos = label_end + 1,
419 }
420
421 let references = state.root_ext.get::<ReferenceMap>()?;
422
423 // covers label === '' and label === undefined
424 // (collapsed reference link and shortcut reference link respectively)
425 let label = if matches!(maybe_label, None | Some("")) {
426 &state.src[label_start..label_end]
427 } else {
428 maybe_label.unwrap()
429 };
430
431 let (destination, title) = references.get(label)?;
432
433 Some(ParseLinkResult {
434 label_start,
435 label_end,
436 href: Some(destination.to_owned()),
437 title: title.map(|s| s.to_owned()),
438 end: pos,
439 })
440}
crates/markdown-it/src/generics/inline/mod.rs created+4
...@@ -0,0 +1,4 @@
1//! Generic inline-level structures.
2pub mod code_pair;
3pub mod emph_pair;
4pub mod full_link;
crates/markdown-it/src/generics/mod.rs created+12
...@@ -0,0 +1,12 @@
1//! Use these to build your own markdown syntax.
2//!
3//! Some markdown structures are very similar under the hood, for example:
4//! - `*emphasis*`, `^supertext^` and `~~strikethrough~~`
5//! - `[link]()` and `![image]()`
6//!
7//! In order to reuse the code between all those, a notion of generic
8//! markdown structures was created. If you want to use syntax like
9//! `=this=` or `++that++`, you only need to specify a character marker
10//! and a renderer function, these rules will figure out the rest.
11//!
12pub mod inline;
crates/markdown-it/src/lib.rs created+29
...@@ -0,0 +1,29 @@
1// for bragging rights
2#![forbid(unsafe_code)]
3//
4// useful asserts that's off by default
5#![warn(clippy::manual_assert)]
6#![warn(clippy::semicolon_if_nothing_returned)]
7//
8// these are often intentionally not collapsed for readability
9#![allow(clippy::collapsible_else_if)]
10#![allow(clippy::collapsible_if)]
11#![allow(clippy::collapsible_match)]
12//
13// these are intentional in bevy systems: nobody is directly calling those,
14// so extra arguments don't decrease readability
15#![allow(clippy::too_many_arguments)]
16#![allow(clippy::type_complexity)]
17//
18// just a style choice that clippy has no business complaining about
19#![allow(clippy::uninlined_format_args)]
20
21pub mod common;
22pub mod examples;
23pub mod generics;
24pub mod parser;
25pub mod plugins;
26
27pub use parser::main::MarkdownIt;
28pub use parser::node::{Node, NodeValue};
29pub use parser::renderer::Renderer;
crates/markdown-it/src/parser/block/builtin/block_parser.rs created+23
...@@ -0,0 +1,23 @@
1use crate::parser::core::{CoreRule, Root};
2use crate::{MarkdownIt, Node};
3
4pub fn add(md: &mut MarkdownIt) {
5 md.add_rule::<BlockParserRule>()
6 .before_all();
7}
8
9pub struct BlockParserRule;
10impl CoreRule for BlockParserRule {
11 fn run(root: &mut Node, md: &MarkdownIt) {
12 let mut node = std::mem::take(root);
13 let data = node.cast_mut::<Root>().unwrap();
14 let source = std::mem::take(&mut data.content);
15 let mut ext = std::mem::take(&mut data.ext);
16
17 node = md.block.parse(source.as_str(), node, md, &mut ext);
18 let data = node.cast_mut::<Root>().unwrap();
19 data.content = source;
20 data.ext = ext;
21 *root = node;
22 }
23}
crates/markdown-it/src/parser/block/builtin/mod.rs created+9
...@@ -0,0 +1,9 @@
1use crate::MarkdownIt;
2
3pub(super) mod block_parser;
4
5pub use block_parser::BlockParserRule;
6
7pub fn add(md: &mut MarkdownIt) {
8 block_parser::add(md);
9}
crates/markdown-it/src/parser/block/mod.rs created+127
...@@ -0,0 +1,127 @@
1//! Block rule chain
2mod state;
3pub use state::*;
4
5mod rule;
6pub use rule::*;
7
8#[doc(hidden)]
9pub mod builtin;
10
11use crate::common::ruler::Ruler;
12use crate::common::TypeKey;
13use crate::parser::extset::RootExtSet;
14use crate::parser::inline::InlineRoot;
15use crate::parser::node::NodeEmpty;
16use crate::{MarkdownIt, Node};
17
18type RuleFns = (
19 fn (&mut BlockState) -> Option<()>,
20 fn (&mut BlockState) -> Option<(Node, usize)>,
21);
22
23#[derive(Debug, Default)]
24/// Block-level tokenizer.
25pub struct BlockParser {
26 ruler: Ruler<TypeKey, RuleFns>,
27}
28
29impl BlockParser {
30 pub fn new() -> Self {
31 Self::default()
32 }
33
34 /// Generate tokens for input range
35 ///
36 pub fn tokenize(&self, state: &mut BlockState) {
37 stacker::maybe_grow(64*1024, 1024*1024, || {
38 let mut has_empty_lines = false;
39
40 while state.line < state.line_max {
41 state.line = state.skip_empty_lines(state.line);
42 if state.line >= state.line_max { break; }
43
44 // Termination condition for nested calls.
45 // Nested calls currently used for blockquotes & lists
46 if state.line_indent(state.line) < 0 { break; }
47
48 // If nesting level exceeded - skip tail to the end. That's not ordinary
49 // situation and we should not care about content.
50 if state.level >= state.md.max_nesting {
51 state.line = state.line_max;
52 break;
53 }
54
55 // Try all possible rules.
56 // On success, rule should:
57 //
58 // - update `state.line`
59 // - update `state.tokens`
60 // - return true
61 let mut ok = None;
62
63 for rule in self.ruler.iter() {
64 ok = rule.1(state);
65 if ok.is_some() {
66 break;
67 }
68 }
69
70 if let Some((mut node, len)) = ok {
71 state.line += len;
72 if !node.is::<NodeEmpty>() {
73 node.srcmap = state.get_map(state.line - len, state.line - 1);
74 state.node.children.push(node);
75 }
76 } else {
77 // this can only happen if user disables paragraph rule
78 // push text as is, this behavior can change in the future;
79 // users should always have some kind of default block rule
80 let mut content = state.get_line(state.line).to_owned();
81 content.push('\n');
82 let node = Node::new(InlineRoot::new(
83 content,
84 vec![(0, state.line_offsets[state.line].first_nonspace)],
85 ));
86 state.node.children.push(node);
87 state.line += 1;
88 }
89
90 // set state.tight if we had an empty line before current tag
91 // i.e. latest empty line should not count
92 state.tight = !has_empty_lines;
93
94 // paragraph might "eat" one newline after it in nested lists
95 if state.is_empty(state.line - 1) {
96 has_empty_lines = true;
97 }
98
99 if state.line < state.line_max && state.is_empty(state.line) {
100 has_empty_lines = true;
101 state.line += 1;
102 }
103 }
104 });
105 }
106
107 /// Process input string and push block tokens into `out_tokens`
108 ///
109 pub fn parse(&self, src: &str, node: Node, md: &MarkdownIt, root_ext: &mut RootExtSet) -> Node {
110 let mut state = BlockState::new(src, md, root_ext, node);
111 self.tokenize(&mut state);
112 state.node
113 }
114
115 pub fn add_rule<T: BlockRule>(&mut self) -> RuleBuilder<RuleFns> {
116 let item = self.ruler.add(TypeKey::of::<T>(), (T::check, T::run));
117 RuleBuilder::new(item)
118 }
119
120 pub fn has_rule<T: BlockRule>(&mut self) -> bool {
121 self.ruler.contains(TypeKey::of::<T>())
122 }
123
124 pub fn remove_rule<T: BlockRule>(&mut self) {
125 self.ruler.remove(TypeKey::of::<T>());
126 }
127}
crates/markdown-it/src/parser/block/rule.rs created+13
...@@ -0,0 +1,13 @@
1use crate::parser::core::rule_builder;
2use crate::Node;
3
4/// Each member of block rule chain must implement this trait
5pub trait BlockRule : 'static {
6 fn check(state: &mut super::BlockState) -> Option<()> {
7 Self::run(state).map(|_| ())
8 }
9
10 fn run(state: &mut super::BlockState) -> Option<(Node, usize)>;
11}
12
13rule_builder!(BlockRule);
crates/markdown-it/src/parser/block/state.rs created+272
...@@ -0,0 +1,272 @@
1// Parser state class
2//
3use crate::common::sourcemap::SourcePos;
4use crate::common::utils::calc_right_whitespace_with_tabstops;
5use crate::parser::extset::RootExtSet;
6use crate::{MarkdownIt, Node};
7
8#[derive(Debug)]
9#[readonly::make]
10/// Sandbox object containing data required to parse block structures.
11pub struct BlockState<'a, 'b> where 'b: 'a {
12 /// Markdown source.
13 #[readonly]
14 pub src: &'b str,
15
16 /// Link to parser instance.
17 #[readonly]
18 pub md: &'a MarkdownIt,
19
20 pub root_ext: &'b mut RootExtSet,
21
22 /// Current node, your rule is supposed to add children to it.
23 pub node: Node,
24
25 pub line_offsets: Vec<LineOffset>,
26
27 /// Current block content indent (for example, if we are
28 /// inside a list, it would be positioned after list marker).
29 pub blk_indent: usize,
30
31 /// Current line in src.
32 pub line: usize,
33
34 /// Maximum allowed line in src.
35 pub line_max: usize,
36
37 /// True if there are no empty lines between paragraphs, used to
38 /// toggle loose/tight mode for lists.
39 pub tight: bool,
40
41 /// indent of the current list block.
42 pub list_indent: Option<u32>,
43
44 pub level: u32,
45}
46
47/// Holds start/end/etc. positions for a specific source text line.
48#[derive(Debug, Clone)]
49pub struct LineOffset {
50 /// `line_start` is the actual start of the line.
51 ///
52 /// # const IGNORE : &str = stringify! {
53 /// " > blockquote\r\n"
54 /// ^-- it will always point here (must not be modified by rules)
55 /// # };
56 pub line_start: usize,
57
58 /// `line_end` is first newline character after the line,
59 /// or position after string length if there aren't any newlines left.
60 ///
61 /// # const IGNORE : &str = stringify! {
62 /// " > blockquote\r\n"
63 /// ^-- it will point here
64 /// # };
65 pub line_end: usize,
66
67 /// `first_nonspace` is the byte offset of the first non-space character in
68 /// the current line.
69 ///
70 /// # const IGNORE : &str = stringify! {
71 /// " > blockquote\r\n"
72 /// ^-- it will point here when paragraph is parsed
73 /// ^----- it is initially pointed here
74 /// # };
75 ///
76 /// It will be modified by rules (list and blockquote), chars before it
77 /// must be treated as whitespaces.
78 ///
79 pub first_nonspace: usize,
80
81 /// `indent_nonspace` is the indent (amount of virtual spaces from start)
82 /// of first non-space character in the current line, taking into account
83 /// tab expansion.
84 ///
85 /// For example, in case of ` \t foo`, indent is 5 (tab ends at multiple of 4,
86 /// then one space after it). Only tabs and spaces are counted for it,
87 /// so no funny unicode business (if cmark supported unicode spaces, they'd
88 /// be counted as 1 each regardless of utf8 width).
89 ///
90 /// You should compare `indent_nonspace` with `state.blkindent` when determining
91 /// real indent after taking into account lists.
92 ///
93 /// Most block rules in commonmark are indented 0..=3, and >=4 is code block.
94 /// Special value of ident_nonspace=-1 is used by this library as a sign
95 /// that this rule can only be a paragraph continuation (used in blockquotes),
96 /// so you must take into account that any math can end up negative.
97 ///
98 pub indent_nonspace: i32,
99}
100
101impl<'a, 'b> BlockState<'a, 'b> {
102 pub fn new(src: &'b str, md: &'a MarkdownIt, root_ext: &'b mut RootExtSet, node: Node) -> Self {
103 let mut result = Self {
104 src,
105 md,
106 root_ext,
107 node,
108 line_offsets: Vec::new(),
109 blk_indent: 0,
110 line: 0,
111 line_max: 0,
112 tight: false,
113 list_indent: None,
114 level: 0,
115 };
116
117 result.generate_caches();
118 result
119 }
120
121 fn generate_caches(&mut self) {
122 // Create caches
123 // Generate markers.
124 let mut chars = self.src.chars().peekable();
125 let mut indent_found = false;
126 let mut indent = 0;
127 let mut offset = 0;
128 let mut start = 0;
129 let mut pos = 0;
130
131 loop {
132 match chars.next() {
133 Some(ch @ (' ' | '\t')) if !indent_found => {
134 indent += 1;
135 offset += if ch == '\t' { 4 - offset % 4 } else { 1 };
136 pos += 1;
137 }
138 ch @ (Some('\n' | '\r') | None) => {
139 self.line_offsets.push(LineOffset {
140 line_start: start,
141 line_end: pos,
142 first_nonspace: start + indent,
143 indent_nonspace: offset,
144 });
145
146 if ch == Some('\r') && chars.peek() == Some(&'\n') {
147 // treat CR+LF as one linebreak
148 chars.next();
149 pos += 1;
150 }
151
152 indent_found = false;
153 indent = 0;
154 offset = 0;
155 start = pos + 1;
156 pos += 1;
157
158 if ch.is_none() || chars.peek().is_none() {
159 break;
160 }
161 }
162 Some(ch) => {
163 indent_found = true;
164 pos += ch.len_utf8();
165 }
166 }
167 }
168
169 self.line_max = self.line_offsets.len();
170 }
171
172 #[must_use]
173 pub fn test_rules_at_line(&mut self) -> bool {
174 for rule in self.md.block.ruler.iter() {
175 if rule.0(self).is_some() {
176 return true;
177 }
178 }
179 false
180 }
181
182 #[must_use]
183 #[inline]
184 pub fn is_empty(&self, line: usize) -> bool {
185 if let Some(offsets) = self.line_offsets.get(line) {
186 offsets.first_nonspace >= offsets.line_end
187 } else {
188 false
189 }
190 }
191
192 pub fn skip_empty_lines(&self, from: usize) -> usize {
193 let mut line = from;
194 while line != self.line_max && self.is_empty(line) {
195 line += 1;
196 }
197 line
198 }
199
200 /// return line indent of specific line, taking into account blockquotes and lists;
201 /// it may be negative if a text has less indentation than current list item
202 #[must_use]
203 #[inline]
204 pub fn line_indent(&self, line: usize) -> i32 {
205 if line < self.line_max {
206 self.line_offsets[line].indent_nonspace - self.blk_indent as i32
207 } else {
208 0
209 }
210 }
211
212 /// return a single line, trimming initial spaces
213 #[must_use]
214 #[inline]
215 pub fn get_line(&self, line: usize) -> &str {
216 if line < self.line_max {
217 let pos = self.line_offsets[line].first_nonspace;
218 let max = self.line_offsets[line].line_end;
219 &self.src[pos..max]
220 } else {
221 ""
222 }
223 }
224
225 /// Cut a range of lines begin..end (not including end) from the source without preceding indent.
226 /// Returns a string (lines) plus a mapping (start of each line in result -> start of each line in source).
227 pub fn get_lines(&self, begin: usize, end: usize, indent: usize, keep_last_lf: bool) -> (String, Vec<(usize, usize)>) {
228 debug_assert!(begin <= end);
229
230 let mut line = begin;
231 let mut result = String::new();
232 let mut mapping = Vec::new();
233
234 while line < end {
235 let offsets = &self.line_offsets[line];
236 let last = offsets.line_end;
237 let add_last_lf = line + 1 < end || keep_last_lf;
238
239 let (num_spaces, first) = calc_right_whitespace_with_tabstops(
240 &self.src[offsets.line_start..offsets.first_nonspace],
241 offsets.indent_nonspace - indent as i32
242 );
243
244 mapping.push(( result.len(), offsets.line_start+first ));
245 result += &" ".repeat(num_spaces);
246 result += &self.src[offsets.line_start+first..last];
247 if add_last_lf { result.push('\n'); }
248 line += 1;
249 }
250
251 ( result, mapping )
252 }
253
254 #[must_use]
255 #[inline]
256 pub fn get_map(&self, start_line: usize, end_line: usize) -> Option<SourcePos> {
257 debug_assert!(start_line <= end_line);
258
259 Some(SourcePos::new(
260 self.line_offsets[start_line].first_nonspace,
261 self.line_offsets[end_line].line_end
262 ))
263 }
264
265 #[must_use]
266 #[inline]
267 pub fn get_map_from_offsets(&self, start_pos: usize, end_pos: usize) -> Option<SourcePos> {
268 debug_assert!(start_pos <= end_pos);
269
270 Some(SourcePos::new(start_pos, end_pos))
271 }
272}
crates/markdown-it/src/parser/core/mod.rs created+6
...@@ -0,0 +1,6 @@
1//! Core rule chain
2mod rule;
3pub use rule::*;
4
5mod root;
6pub use root::*;
crates/markdown-it/src/parser/core/root.rs created+21
...@@ -0,0 +1,21 @@
1use crate::parser::extset::RootExtSet;
2use crate::{Node, NodeValue, Renderer};
3
4#[derive(Debug)]
5/// Root node of the AST.
6pub struct Root {
7 pub content: String,
8 pub ext: RootExtSet,
9}
10
11impl Root {
12 pub fn new(content: String) -> Self {
13 Self { content, ext: RootExtSet::new() }
14 }
15}
16
17impl NodeValue for Root {
18 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
19 fmt.contents(&node.children);
20 }
21}
crates/markdown-it/src/parser/core/rule.rs created+55
...@@ -0,0 +1,55 @@
1use crate::{MarkdownIt, Node};
2
3/// Each member of core rule chain must implement this trait
4pub trait CoreRule : 'static {
5 fn run(root: &mut Node, md: &MarkdownIt);
6}
7
8macro_rules! rule_builder {
9 ($var: ident) => {
10 /// Adjust positioning of a newly added rule in the chain.
11 pub struct RuleBuilder<'a, T> {
12 item: &'a mut crate::common::ruler::RuleItem<crate::common::TypeKey, T>
13 }
14
15 impl<'a, T> RuleBuilder<'a, T> {
16 pub(crate) fn new(item: &'a mut crate::common::ruler::RuleItem<crate::common::TypeKey, T>) -> Self {
17 Self { item }
18 }
19
20 pub fn before<U: $var>(self) -> Self {
21 self.item.before(crate::common::TypeKey::of::<U>());
22 self
23 }
24
25 pub fn after<U: $var>(self) -> Self {
26 self.item.after(crate::common::TypeKey::of::<U>());
27 self
28 }
29
30 pub fn before_all(self) -> Self {
31 self.item.before_all();
32 self
33 }
34
35 pub fn after_all(self) -> Self {
36 self.item.after_all();
37 self
38 }
39
40 pub fn alias<U: $var>(self) -> Self {
41 self.item.alias(crate::common::TypeKey::of::<U>());
42 self
43 }
44
45 pub fn require<U: $var>(self) -> Self {
46 self.item.require(crate::common::TypeKey::of::<U>());
47 self
48 }
49 }
50 };
51}
52
53rule_builder!(CoreRule);
54
55pub(crate) use rule_builder;
crates/markdown-it/src/parser/extset.rs created+232
...@@ -0,0 +1,232 @@
1//! Extension sets
2//!
3//! These things allow you to put custom data inside internal markdown-it structures.
4//!
5use downcast_rs::{impl_downcast, Downcast};
6use std::fmt::Debug;
7
8/// Extension set member for the entire parser (only writable at init).
9pub trait MarkdownItExt : Debug + Downcast + Send + Sync {}
10impl_downcast!(MarkdownItExt);
11extension_set!(MarkdownItExtSet, MarkdownItExt);
12
13/// Extension set member for an arbitrary AST node.
14pub trait NodeExt : Debug + Downcast + Send + Sync {}
15impl_downcast!(NodeExt);
16extension_set!(NodeExtSet, NodeExt);
17
18/// Extension set member for an inline context.
19pub trait InlineRootExt : Debug + Downcast + Send + Sync {}
20impl_downcast!(InlineRootExt);
21extension_set!(InlineRootExtSet, InlineRootExt);
22
23/// Extension set member for a block context.
24pub trait RootExt : Debug + Downcast + Send + Sync {}
25impl_downcast!(RootExt);
26extension_set!(RootExtSet, RootExt);
27
28/// Extension set member for a renderer context.
29pub trait RenderExt : Debug + Downcast + Send + Sync {}
30impl_downcast!(RenderExt);
31extension_set!(RenderExtSet, RenderExt);
32
33// see https://github.com/malobre/erased_set for inspiration and API
34// see https://lucumr.pocoo.org/2022/1/7/as-any-hack/ for additional impl details
35macro_rules! extension_set {
36 ($name: ident, $trait: ident) => {
37 #[derive(Debug, Default)]
38 pub struct $name(::std::collections::HashMap<crate::common::TypeKey, Box<dyn $trait>>);
39
40 impl $name {
41 #[must_use]
42 pub fn new() -> Self {
43 Self::default()
44 }
45
46 #[must_use]
47 pub fn is_empty(&self) -> bool {
48 self.0.is_empty()
49 }
50
51 #[must_use]
52 pub fn len(&self) -> usize {
53 self.0.len()
54 }
55
56 pub fn clear(&mut self) {
57 self.0.clear();
58 }
59
60 #[must_use]
61 pub fn contains<T: 'static>(&self) -> bool {
62 let key = crate::common::TypeKey::of::<T>();
63 self.0.contains_key(&key)
64 }
65
66 #[must_use]
67 pub fn get<T: $trait>(&self) -> Option<&T> {
68 let key = crate::common::TypeKey::of::<T>();
69 let result = self.0.get(&key)?;
70 result.downcast_ref::<T>()
71 }
72
73 #[must_use]
74 pub fn get_mut<T: $trait>(&mut self) -> Option<&mut T> {
75 let key = crate::common::TypeKey::of::<T>();
76 let result = self.0.get_mut(&key)?;
77 result.downcast_mut::<T>()
78 }
79
80 pub fn get_or_insert<T: $trait>(&mut self, value: T) -> &mut T {
81 let key = crate::common::TypeKey::of::<T>();
82 let result = self.0.entry(key).or_insert_with(|| Box::new(value));
83 result.downcast_mut::<T>().unwrap()
84 }
85
86 pub fn get_or_insert_with<T: $trait>(&mut self, f: impl FnOnce() -> T) -> &mut T {
87 let key = crate::common::TypeKey::of::<T>();
88 let result = self.0.entry(key).or_insert_with(|| Box::new(f()));
89 result.downcast_mut::<T>().unwrap()
90 }
91
92 pub fn get_or_insert_default<T: $trait + Default>(&mut self) -> &mut T {
93 let key = crate::common::TypeKey::of::<T>();
94 let result = self.0.entry(key).or_insert_with(|| Box::<T>::default());
95 result.downcast_mut::<T>().unwrap()
96 }
97
98 pub fn insert<T: $trait>(&mut self, value: T) -> Option<T> {
99 let key = crate::common::TypeKey::of::<T>();
100 let result = self.0.insert(key, Box::new(value))?;
101 Some(*result.downcast::<T>().unwrap())
102 }
103
104 pub fn remove<T: $trait>(&mut self) -> Option<T> {
105 let key = crate::common::TypeKey::of::<T>();
106 let result = self.0.remove(&key)?;
107 Some(*result.downcast::<T>().unwrap())
108 }
109 }
110 }
111}
112
113pub(crate) use extension_set;
114
115#[cfg(test)]
116mod tests {
117 use super::extension_set;
118 use downcast_rs::{Downcast, impl_downcast};
119 use std::fmt::Debug;
120
121 pub trait TestExt : Debug + Downcast + Send + Sync {}
122 impl_downcast!(TestExt);
123
124 extension_set!(TestExtSet, TestExt);
125
126 impl<T: Debug + Downcast + Send + Sync> TestExt for T {}
127
128 #[test]
129 fn empty_set() {
130 let set = TestExtSet::new();
131 assert_eq!(set.len(), 0);
132 assert!(set.is_empty());
133 }
134
135 #[test]
136 fn insert_elements() {
137 let mut set = TestExtSet::new();
138 set.insert(42u8);
139 assert_eq!(set.len(), 1);
140 assert!(!set.is_empty());
141 set.insert(42u16);
142 assert_eq!(set.len(), 2);
143 assert!(!set.is_empty());
144 }
145
146 #[test]
147 fn contains() {
148 let mut set = TestExtSet::new();
149 set.insert(42u8);
150 assert!(!set.contains::<u16>());
151 set.insert(42u16);
152 assert!(set.contains::<u16>());
153 set.remove::<u16>();
154 assert!(!set.contains::<u16>());
155 }
156
157 #[test]
158 fn get() {
159 let mut set = TestExtSet::new();
160 set.insert(42u8);
161 assert_eq!(set.get::<u16>(), None);
162 set.insert(42u16);
163 set.insert(123u16);
164 assert_eq!(set.get::<u16>(), Some(&123u16));
165 }
166
167 #[test]
168 fn get_mut() {
169 let mut set = TestExtSet::new();
170 set.insert(42u16);
171 *set.get_mut::<u16>().unwrap() = 123u16;
172 assert_eq!(set.get::<u16>(), Some(&123u16));
173 }
174
175 #[test]
176 fn or_insert() {
177 let mut set = TestExtSet::new();
178 set.insert(123u8);
179 assert_eq!(set.get_or_insert(0u8), &mut 123u8);
180 assert_eq!(set.get_or_insert_default::<u8>(), &mut 123u8);
181 assert_eq!(set.get_or_insert_with(|| 0u8), &mut 123u8);
182 set.clear();
183 assert_eq!(set.get_or_insert(10u8), &mut 10u8);
184 set.clear();
185 assert_eq!(set.get_or_insert_with(|| 20u8), &mut 20u8);
186 set.clear();
187 assert_eq!(set.get_or_insert_default::<u8>(), &mut 0u8);
188 }
189
190 #[test]
191 fn different_types_stored_once() {
192 let mut set = TestExtSet::new();
193 set.insert("foo");
194 set.insert("bar");
195 set.insert("quux");
196 assert_eq!(set.len(), 1);
197 }
198
199 #[test]
200 fn zero_sized_types() {
201 #[derive(Debug, PartialEq, Eq)]
202 struct A;
203 #[derive(Debug, PartialEq, Eq)]
204 struct B;
205 let mut set = TestExtSet::new();
206 set.insert(A);
207 set.insert(B);
208 assert_eq!(set.len(), 2);
209 assert_eq!(set.get::<A>(), Some(&A));
210 }
211
212 #[test]
213 fn clear() {
214 let mut set = TestExtSet::new();
215 set.insert(42u8);
216 set.insert(42u16);
217 assert_eq!(set.len(), 2);
218 set.clear();
219 assert_eq!(set.len(), 0);
220 }
221
222 #[test]
223 fn debug() {
224 let mut set = TestExtSet::new();
225 set.insert(42);
226 set.insert("test");
227 let str = format!("{:?}", set);
228 // there are no guarantees about field order, so check both
229 assert!(str == "TestExtSet({i32: 42, &str: \"test\"})" ||
230 str == "TestExtSet({&str: \"test\", i32: 42})");
231 }
232}
crates/markdown-it/src/parser/inline/builtin/inline_parser.rs created+80
...@@ -0,0 +1,80 @@
1use crate::parser::block::builtin::BlockParserRule;
2use crate::parser::core::{CoreRule, Root};
3use crate::parser::extset::{InlineRootExtSet, RootExtSet};
4use crate::{MarkdownIt, Node, NodeValue};
5
6#[derive(Debug)]
7/// Temporary node which gets replaced with inline nodes when
8/// [InlineParser](crate::parser::inline::InlineParser) is called.
9pub struct InlineRoot {
10 pub content: String,
11 pub mapping: Vec<(usize, usize)>,
12 pub ext: InlineRootExtSet,
13}
14
15impl InlineRoot {
16 pub fn new(content: String, mapping: Vec<(usize, usize)>) -> Self {
17 Self { content, mapping, ext: InlineRootExtSet::new() }
18 }
19}
20
21// this token is supposed to be replaced by one or many actual tokens by inline rule
22impl NodeValue for InlineRoot {}
23
24pub fn add(md: &mut MarkdownIt) {
25 md.add_rule::<InlineParserRule>()
26 .after::<BlockParserRule>()
27 .before_all();
28}
29
30pub struct InlineParserRule;
31impl CoreRule for InlineParserRule {
32 fn run(root: &mut Node, md: &MarkdownIt) {
33 fn walk_recursive(node: &mut Node, md: &MarkdownIt, root_ext: &mut RootExtSet) {
34 let mut idx = 0;
35 while idx < node.children.len() {
36 let child = &mut node.children[idx];
37 if let Some(data) = child.cast_mut::<InlineRoot>() {
38 let content = std::mem::take(&mut data.content);
39 let mapping = std::mem::take(&mut data.mapping);
40 let mut inline_ext = std::mem::take(&mut data.ext);
41
42 let mut root = std::mem::take(child);
43 root.ext = std::mem::take(&mut node.ext);
44 root.children = Vec::new();
45 root = md.inline.parse(content, mapping, root, md, root_ext, &mut inline_ext);
46
47 let len = root.children.len();
48 node.children.splice(idx..=idx, std::mem::take(&mut root.children));
49 node.ext = std::mem::take(&mut root.ext);
50 idx += len;
51 } else {
52 stacker::maybe_grow(64*1024, 1024*1024, || {
53 walk_recursive(child, md, root_ext);
54 });
55 idx += 1;
56 }
57 }
58 }
59
60 let data = root.cast_mut::<Root>().unwrap();
61 let mut root_ext = std::mem::take(&mut data.ext);
62
63 // this is invalid if input only contains reference;
64 // so if user disables block parser, he must insert smth like this instead
65 /*if root.children.is_empty() {
66 // block parser disabled, parse as if input was one big inline block
67 let data = root.cast_mut::<Root>().unwrap();
68 let node = Node::new(InlineRoot {
69 content: data.content.clone(),
70 mapping: vec![(0, 0)],
71 });
72 root.children.push(node);
73 }*/
74
75 walk_recursive(root, md, &mut root_ext);
76
77 let data = root.cast_mut::<Root>().unwrap();
78 data.ext = root_ext;
79 }
80}
crates/markdown-it/src/parser/inline/builtin/mod.rs created+12
...@@ -0,0 +1,12 @@
1use crate::MarkdownIt;
2
3pub(super) mod inline_parser;
4pub(super) mod skip_text;
5
6pub use inline_parser::InlineParserRule;
7pub use skip_text::TextScanner;
8
9pub fn add(md: &mut MarkdownIt) {
10 skip_text::add(md);
11 inline_parser::add(md);
12}
crates/markdown-it/src/parser/inline/builtin/skip_text.rs created+140
...@@ -0,0 +1,140 @@
1//! Skip text characters for text token, place those to pending buffer
2//! and increment current pos
3//!
4use regex::{self, Regex};
5
6use crate::parser::inline::{InlineRule, InlineState};
7use crate::{MarkdownIt, Node, NodeValue, Renderer};
8
9#[derive(Debug)]
10/// Plain text AST node.
11pub struct Text {
12 pub content: String
13}
14
15impl NodeValue for Text {
16 fn render(&self, _: &Node, fmt: &mut dyn Renderer) {
17 fmt.text(&self.content);
18 }
19}
20
21#[derive(Debug)]
22/// Escaped text AST node (backslash escapes and entities).
23pub struct TextSpecial {
24 pub content: String,
25 pub markup: String,
26 pub info: &'static str,
27}
28
29impl NodeValue for TextSpecial {
30 fn render(&self, _: &Node, fmt: &mut dyn Renderer) {
31 fmt.text(&self.content);
32 }
33}
34
35pub fn add(md: &mut MarkdownIt) {
36 md.inline.add_rule::<TextScanner>()
37 .before_all();
38}
39
40#[derive(Debug)]
41pub(crate) enum TextScannerImpl {
42 SkipPunct,
43 SkipRegex(Regex),
44}
45
46/// Rule to skip pure text
47/// '{}$%@~+=:' reserved for extensions
48///
49/// !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, :, ;, <, =, >, ?, @, [, \, ], ^, _, `, {, |, }, or ~
50///
51/// !!!! Don't confuse with "Markdown ASCII Punctuation" chars
52/// http://spec.commonmark.org/0.15/#ascii-punctuation-character
53///
54pub struct TextScanner;
55
56impl TextScanner {
57 fn choose_text_impl(charmap: Vec<char>) -> TextScannerImpl {
58 let mut can_use_punct = true;
59 for ch in charmap.iter() {
60 match ch {
61 '\n' | '!' | '#' | '$' | '%' | '&' | '*' | '+' | '-' |
62 ':' | '<' | '=' | '>' | '@' | '[' | '\\' | ']' | '^' |
63 '_' | '`' | '{' | '}' | '~' => {},
64 _ => {
65 can_use_punct = false;
66 break;
67 }
68 }
69 }
70
71 if can_use_punct {
72 TextScannerImpl::SkipPunct
73 } else {
74 TextScannerImpl::SkipRegex(
75 Regex::new(
76 // [] panics on "unclosed character class", but it cannot happen here
77 // (we'd use punct rule instead)
78 &format!("^[^{}]+", charmap.into_iter().map(
79 |c| regex::escape(&c.to_string())
80 ).collect::<String>())
81 ).unwrap()
82 )
83 }
84 }
85
86 fn find_text_length(state: &mut InlineState) -> usize {
87 let text_impl = state.md.inline.text_impl.get_or_init(
88 || Self::choose_text_impl(state.md.inline.text_charmap.keys().copied().collect())
89 );
90
91 let mut len = 0;
92
93 match text_impl {
94 TextScannerImpl::SkipPunct => {
95 let mut chars = state.src[state.pos..state.pos_max].chars();
96
97 loop {
98 match chars.next() {
99 Some(
100 '\n' | '!' | '#' | '$' | '%' | '&' | '*' | '+' | '-' |
101 ':' | '<' | '=' | '>' | '@' | '[' | '\\' | ']' | '^' |
102 '_' | '`' | '{' | '}' | '~'
103 ) => {
104 break;
105 }
106 Some(chr) => {
107 len += chr.len_utf8();
108 }
109 None => { break; }
110 }
111 }
112 }
113 TextScannerImpl::SkipRegex(re) => {
114 if let Some(capture) = re.find(&state.src[state.pos..state.pos_max]) {
115 len = capture.end();
116 }
117 }
118 }
119
120 len
121 }
122}
123
124impl InlineRule for TextScanner {
125 const MARKER: char = '\0';
126
127 fn check(state: &mut InlineState) -> Option<usize> {
128 let len = Self::find_text_length(state);
129 if len == 0 { return None; }
130 Some(len)
131 }
132
133 fn run(state: &mut InlineState) -> Option<(Node, usize)> {
134 let len = Self::find_text_length(state);
135 if len == 0 { return None; }
136 state.trailing_text_push(state.pos, state.pos + len);
137 state.pos += len;
138 Some((Node::default(), 0))
139 }
140}
crates/markdown-it/src/parser/inline/mod.rs created+162
...@@ -0,0 +1,162 @@
1//! Inline rule chain
2use once_cell::sync::OnceCell;
3use std::collections::HashMap;
4
5mod state;
6pub use state::*;
7
8mod rule;
9pub use rule::*;
10
11#[doc(hidden)]
12pub mod builtin;
13
14pub use builtin::inline_parser::InlineRoot;
15pub use builtin::skip_text::{Text, TextSpecial};
16use builtin::skip_text::TextScannerImpl;
17
18use crate::{MarkdownIt, Node};
19use crate::common::TypeKey;
20use crate::common::ruler::Ruler;
21use crate::parser::extset::{InlineRootExtSet, RootExtSet};
22
23use super::node::NodeEmpty;
24
25type RuleFns = (
26 fn (&mut InlineState) -> Option<usize>,
27 fn (&mut InlineState) -> Option<(Node, usize)>,
28);
29
30#[derive(Debug, Default)]
31/// Inline-level tokenizer.
32pub struct InlineParser {
33 ruler: Ruler<TypeKey, RuleFns>,
34 text_charmap: HashMap<char, Vec<TypeKey>>,
35 text_impl: OnceCell<TextScannerImpl>,
36}
37
38impl InlineParser {
39 pub fn new() -> Self {
40 Self::default()
41 }
42
43 /// Skip single token by running all rules in validation mode;
44 /// returns `true` if any rule reported success
45 ///
46 pub fn skip_token(&self, state: &mut InlineState) {
47 stacker::maybe_grow(64*1024, 1024*1024, || {
48 let mut ok = None;
49
50 if state.level < state.md.max_nesting {
51 for rule in self.ruler.iter() {
52 ok = rule.0(state);
53 if ok.is_some() {
54 break;
55 }
56 }
57 } else {
58 // Too much nesting, just skip until the end of the paragraph.
59 //
60 // NOTE: this will cause links to behave incorrectly in the following case,
61 // when an amount of `[` is exactly equal to `maxNesting + 1`:
62 //
63 // [[[[[[[[[[[[[[[[[[[[[foo]()
64 //
65 // TODO: remove this workaround when CM standard will allow nested links
66 // (we can replace it by preventing links from being parsed in
67 // validation mode)
68 //
69 state.pos = state.pos_max;
70 }
71
72 if let Some(len) = ok {
73 state.pos += len;
74 } else {
75 let ch = state.src[state.pos..state.pos_max].chars().next().unwrap();
76 state.pos += ch.len_utf8();
77 }
78 });
79 }
80
81 /// Generate tokens for input range
82 ///
83 pub fn tokenize(&self, state: &mut InlineState) {
84 stacker::maybe_grow(64*1024, 1024*1024, || {
85 let end = state.pos_max;
86
87 while state.pos < end {
88 // Try all possible rules.
89 // On success, rule should:
90 //
91 // - update `state.pos`
92 // - update `state.tokens`
93 // - return true
94 let mut ok = None;
95
96 if state.level < state.md.max_nesting {
97 for rule in self.ruler.iter() {
98 ok = rule.1(state);
99 if ok.is_some() {
100 break;
101 }
102 }
103 }
104
105 if let Some((mut node, len)) = ok {
106 state.pos += len;
107 if !node.is::<NodeEmpty>() {
108 node.srcmap = state.get_map(state.pos - len, state.pos);
109 state.node.children.push(node);
110 if state.pos >= end { break; }
111 }
112 continue;
113 }
114
115 let ch = state.src[state.pos..state.pos_max].chars().next().unwrap();
116 let len = ch.len_utf8();
117 state.trailing_text_push(state.pos, state.pos + len);
118 state.pos += len;
119 }
120 });
121 }
122
123 /// Process input string and push inline tokens into `out_tokens`
124 ///
125 pub fn parse(
126 &self,
127 src: String,
128 srcmap: Vec<(usize, usize)>,
129 node: Node,
130 md: &MarkdownIt,
131 root_ext: &mut RootExtSet,
132 inline_ext: &mut InlineRootExtSet,
133 ) -> Node {
134 let mut state = InlineState::new(src, srcmap, md, root_ext, inline_ext, node);
135 self.tokenize(&mut state);
136 state.node
137 }
138
139 pub fn add_rule<T: InlineRule>(&mut self) -> RuleBuilder<RuleFns> {
140 if T::MARKER != '\0' {
141 let charvec = self.text_charmap.entry(T::MARKER).or_default();
142 charvec.push(TypeKey::of::<T>());
143 }
144
145 let item = self.ruler.add(TypeKey::of::<T>(), (T::check, T::run));
146 RuleBuilder::new(item)
147 }
148
149 pub fn has_rule<T: InlineRule>(&mut self) -> bool {
150 self.ruler.contains(TypeKey::of::<T>())
151 }
152
153 pub fn remove_rule<T: InlineRule>(&mut self) {
154 if T::MARKER != '\0' {
155 let mut charvec = self.text_charmap.remove(&T::MARKER).unwrap_or_default();
156 charvec.retain(|x| *x != TypeKey::of::<T>());
157 self.text_charmap.insert(T::MARKER, charvec);
158 }
159
160 self.ruler.remove(TypeKey::of::<T>());
161 }
162}
crates/markdown-it/src/parser/inline/rule.rs created+15
...@@ -0,0 +1,15 @@
1use crate::parser::core::rule_builder;
2use crate::Node;
3
4/// Each member of inline rule chain must implement this trait
5pub trait InlineRule : 'static {
6 const MARKER: char;
7
8 fn check(state: &mut super::InlineState) -> Option<usize> {
9 Self::run(state).map(|(_node, len)| len)
10 }
11
12 fn run(state: &mut super::InlineState) -> Option<(Node, usize)>;
13}
14
15rule_builder!(InlineRule);
crates/markdown-it/src/parser/inline/state.rs created+244
...@@ -0,0 +1,244 @@
1// Inline parser state
2//
3use crate::common::sourcemap::SourcePos;
4use crate::common::utils::is_punct_char;
5use crate::parser::extset::{InlineRootExtSet, RootExtSet};
6use crate::parser::inline::Text;
7use crate::{MarkdownIt, Node};
8
9#[derive(Debug, Clone, Copy)]
10/// Information about emphasis delimiter run returned from [InlineState::scan_delims].
11pub struct DelimiterRun {
12 /// Starting marker character.
13 pub marker: char,
14
15 /// Boolean flag that determines if this delimiter could open an emphasis.
16 pub can_open: bool,
17
18 /// Boolean flag that determines if this delimiter could open an emphasis.
19 pub can_close: bool,
20
21 /// Total length of scanned delimiters.
22 pub length: usize,
23}
24
25#[derive(Debug)]
26#[readonly::make]
27/// Sandbox object containing data required to parse inline structures.
28pub struct InlineState<'a, 'b> where 'b: 'a {
29 /// Markdown source.
30 #[readonly]
31 pub src: String,
32
33 /// Link to parser instance.
34 #[readonly]
35 pub md: &'a MarkdownIt,
36
37 /// Current node, your rule is supposed to add children to it.
38 pub node: Node,
39
40 /// For each line, it holds offset of the start of the line in original
41 /// markdown source and offset of the start of the line in `src`.
42 pub srcmap: Vec<(usize, usize)>,
43 pub root_ext: &'b mut RootExtSet,
44 pub inline_ext: &'b mut InlineRootExtSet,
45
46 /// Current byte offset in `src`, it must respect char boundaries.
47 pub pos: usize,
48
49 /// Maximum allowed byte offset in `src`, it must respect char boundaries.
50 pub pos_max: usize,
51
52 /// Counter used to disable inline linkifier execution
53 /// inside raw html and markdown links.
54 pub link_level: i32,
55
56 /// Counter used to prevent recursion by image and link rules.
57 pub level: u32,
58}
59
60impl<'a, 'b> InlineState<'a, 'b> {
61 pub fn new(
62 src: String,
63 srcmap: Vec<(usize, usize)>,
64 md: &'a MarkdownIt,
65 root_ext: &'b mut RootExtSet,
66 inline_ext: &'b mut InlineRootExtSet,
67 node: Node,
68 ) -> Self {
69 let mut result = Self {
70 pos: 0,
71 pos_max: src.len(),
72 src,
73 srcmap,
74 root_ext,
75 inline_ext,
76 md,
77 node,
78 link_level: 0,
79 level: 0,
80 };
81
82 result.trim_src();
83 result
84 }
85
86 fn trim_src(&mut self) {
87 let mut chars = self.src.as_bytes().iter();
88 while let Some(b' ' | b'\t') = chars.next_back() {
89 self.pos_max -= 1;
90 }
91 while let Some(b' ' | b'\t') = chars.next() {
92 self.pos += 1;
93 }
94 }
95
96 pub fn trailing_text_push(&mut self, start: usize, end: usize) {
97 if let Some(text) = self.node.children.last_mut()
98 .and_then(|t| t.cast_mut::<Text>()) {
99 text.content.push_str(&self.src[start..end]);
100
101 if let Some(map) = self.node.children.last_mut().unwrap().srcmap {
102 let (map_start, _) = map.get_byte_offsets();
103 let map_end = self.get_source_pos_for(end);
104 self.node.children.last_mut().unwrap().srcmap = Some(SourcePos::new(map_start, map_end));
105 }
106 } else {
107 let mut node = Node::new(Text { content: self.src[start..end].to_owned() });
108 node.srcmap = self.get_map(start, end);
109 self.node.children.push(node);
110 }
111 }
112
113 pub fn trailing_text_pop(&mut self, count: usize) {
114 if count == 0 { return; }
115
116 let mut node = self.node.children.pop().unwrap();
117 let text = node.cast_mut::<Text>().unwrap();
118 if text.content.len() == count {
119 // do nothing, just remove the node
120 drop(node);
121 } else {
122 // modify the token and reinsert it later
123 text.content.truncate(text.content.len() - count);
124 if let Some(map) = node.srcmap {
125 let (map_start, map_end) = map.get_byte_offsets();
126 let map_end = self.get_source_pos_for(map_end - count);
127 node.srcmap = Some(SourcePos::new(map_start, map_end));
128 }
129 self.node.children.push(node);
130 }
131 }
132
133 #[must_use]
134 pub fn trailing_text_get(&self) -> &str {
135 if let Some(text) = self.node.children.last()
136 .and_then(|t| t.cast::<Text>()) {
137 text.content.as_str()
138 } else {
139 ""
140 }
141 }
142
143 /// Scan a sequence of emphasis-like markers, and determine whether
144 /// it can start an emphasis sequence or end an emphasis sequence.
145 ///
146 /// - start - position to scan from (it should point at a valid marker);
147 /// - can_split_word - determine if these markers can be found inside a word
148 ///
149 #[must_use]
150 pub fn scan_delims(&self, start: usize, can_split_word: bool) -> DelimiterRun {
151 let mut left_flanking = true;
152 let mut right_flanking = true;
153
154 let last_char = if start > 0 {
155 self.src[..start].chars().next_back().unwrap()
156 } else {
157 // treat beginning of the line as a whitespace
158 ' '
159 };
160
161 let mut chars = self.src[start..self.pos_max].chars();
162 let marker = chars.next().unwrap();
163 let next_char;
164 let mut count = 1;
165
166 loop {
167 match chars.next() {
168 None => {
169 next_char = ' ';
170 break;
171 }
172 Some(x) => {
173 if x != marker {
174 // treat end of the line as a whitespace
175 next_char = x;
176 break;
177 }
178 }
179 }
180 count += 1;
181 }
182
183 let is_last_punct_char = last_char.is_ascii_punctuation() || is_punct_char(last_char);
184 let is_next_punct_char = next_char.is_ascii_punctuation() || is_punct_char(next_char);
185
186 let is_last_whitespace = last_char.is_whitespace();
187 let is_next_whitespace = next_char.is_whitespace();
188
189 #[allow(clippy::collapsible_if)]
190 if is_next_whitespace {
191 left_flanking = false;
192 } else if is_next_punct_char {
193 if !(is_last_whitespace || is_last_punct_char) {
194 left_flanking = false;
195 }
196 }
197
198 #[allow(clippy::collapsible_if)]
199 if is_last_whitespace {
200 right_flanking = false;
201 } else if is_last_punct_char {
202 if !(is_next_whitespace || is_next_punct_char) {
203 right_flanking = false;
204 }
205 }
206
207 let can_open;
208 let can_close;
209
210 if !can_split_word {
211 can_open = left_flanking && (!right_flanking || is_last_punct_char);
212 can_close = right_flanking && (!left_flanking || is_next_punct_char);
213 } else {
214 can_open = left_flanking;
215 can_close = right_flanking;
216 }
217
218 DelimiterRun {
219 marker,
220 can_open,
221 can_close,
222 length: count
223 }
224 }
225
226 #[must_use]
227 fn get_source_pos_for(&self, pos: usize) -> usize {
228 let line = match self.srcmap.binary_search_by(|x| x.0.cmp(&pos)) {
229 Ok(x) => x,
230 Err(x) => x - 1,
231 };
232 self.srcmap[line].1 + (pos - self.srcmap[line].0)
233 }
234
235 #[must_use]
236 pub fn get_map(&self, start_pos: usize, end_pos: usize) -> Option<SourcePos> {
237 debug_assert!(start_pos <= end_pos);
238
239 Some(SourcePos::new(
240 self.get_source_pos_for(start_pos),
241 self.get_source_pos_for(end_pos)
242 ))
243 }
244}
crates/markdown-it/src/parser/linkfmt.rs created+100
...@@ -0,0 +1,100 @@
1//! Link validator and formatter
2
3use once_cell::sync::Lazy;
4use regex::Regex;
5use std::fmt::Debug;
6
7pub trait LinkFormatter : Debug + Send + Sync {
8 /// Validate link url, return `Some(())` if it is allowed
9 /// and `None` if it is a security risk.
10 fn validate_link(&self, url: &str) -> Option<()>;
11
12 /// Encode link url to a machine-readable format,
13 /// which includes url-encoding, punycode, etc.
14 fn normalize_link(&self, url: &str) -> String;
15
16 /// Decode link url to a human-readable format.
17 fn normalize_link_text(&self, url: &str) -> String;
18}
19
20/// Default link validator and formatter for markdown-it.
21///
22/// This validator can prohibit more than really needed to prevent XSS. It's a
23/// tradeoff to keep code simple and to be secure by default.
24///
25/// If you need different setup - override validator method as you wish. Or
26/// replace it with dummy function and use external sanitizer.
27///
28#[derive(Default, Debug)]
29pub struct MDLinkFormatter;
30
31impl MDLinkFormatter {
32 pub fn new() -> Self {
33 Self
34 }
35}
36
37impl LinkFormatter for MDLinkFormatter {
38 fn validate_link(&self, url: &str) -> Option<()> {
39 // url should be normalized at this point, and existing entities are decoded
40 static BAD_PROTO_RE : Lazy<Regex> = Lazy::new(||
41 Regex::new(r#"(?i)^(vbscript|javascript|file|data):"#).unwrap()
42 );
43
44 static GOOD_DATA_RE : Lazy<Regex> = Lazy::new(||
45 Regex::new(r#"(?i)^data:image/(gif|png|jpeg|webp);"#).unwrap()
46 );
47
48 if !BAD_PROTO_RE.is_match(url) || GOOD_DATA_RE.is_match(url) {
49 Some(())
50 } else {
51 None
52 }
53 }
54
55 fn normalize_link(&self, url: &str) -> String {
56 mdurl::urlencode::encode(url, mdurl::urlencode::ENCODE_DEFAULT_CHARS, true).into()
57 }
58
59 fn normalize_link_text(&self, url: &str) -> String {
60 url.to_owned()
61 }
62}
63
64
65#[cfg(test)]
66mod tests {
67 use super::LinkFormatter;
68 use super::MDLinkFormatter;
69
70 #[test]
71 fn should_allow_normal_urls() {
72 let fmt = MDLinkFormatter::new();
73 assert!(fmt.validate_link("http://example.org").is_some());
74 assert!(fmt.validate_link("HTTPS://example.org").is_some());
75 }
76
77 #[test]
78 fn should_allow_plain_text() {
79 let fmt = MDLinkFormatter::new();
80 assert!(fmt.validate_link("javascript").is_some());
81 assert!(fmt.validate_link("/javascript:link").is_some());
82 }
83
84 #[test]
85 fn should_not_allow_some_protocols() {
86 let fmt = MDLinkFormatter::new();
87 assert!(fmt.validate_link("javascript:alert(1)").is_none());
88 assert!(fmt.validate_link("JAVASCRIPT:alert(1)").is_none());
89 assert!(fmt.validate_link("vbscript:alert(1)").is_none());
90 assert!(fmt.validate_link("VbScript:alert(1)").is_none());
91 assert!(fmt.validate_link("file:///123").is_none());
92 }
93
94 #[test]
95 fn should_not_allow_data_url_except_whitelisted() {
96 let fmt = MDLinkFormatter::new();
97 assert!(fmt.validate_link("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7").is_some());
98 assert!(fmt.validate_link("data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K").is_none());
99 }
100}
crates/markdown-it/src/parser/main.rs created+90
...@@ -0,0 +1,90 @@
1use derivative::Derivative;
2
3use crate::common::ruler::Ruler;
4use crate::common::sourcemap::SourcePos;
5use crate::common::TypeKey;
6use crate::parser::block::{self, BlockParser};
7use crate::parser::core::{Root, *};
8use crate::parser::extset::MarkdownItExtSet;
9use crate::parser::inline::{self, InlineParser};
10use crate::parser::linkfmt::{LinkFormatter, MDLinkFormatter};
11use crate::Node;
12
13type RuleFn = fn (&mut Node, &MarkdownIt);
14
15#[derive(Derivative)]
16#[derivative(Debug)]
17/// Main parser struct, created once and reused for parsing multiple documents.
18pub struct MarkdownIt {
19 /// Block-level tokenizer.
20 pub block: BlockParser,
21
22 /// Inline-level tokenizer.
23 pub inline: InlineParser,
24
25 /// Link validator and formatter.
26 pub link_formatter: Box<dyn LinkFormatter>,
27
28 /// Storage for custom data used in plugins.
29 pub ext: MarkdownItExtSet,
30
31 /// Maximum depth of the generated AST, exists to prevent recursion
32 /// (if markdown source reaches this depth, deeply nested structures
33 /// will be parsed as plain text).
34 /// TODO: doesn't work
35 #[doc(hidden)]
36 pub max_nesting: u32,
37
38 /// Maximum allowed indentation for syntax blocks
39 /// default i32::MAX, indented code blocks will set this to 4
40 pub max_indent: i32,
41
42 ruler: Ruler<TypeKey, RuleFn>,
43}
44
45impl MarkdownIt {
46 pub fn new() -> Self {
47 Self::default()
48 }
49
50 pub fn parse(&self, src: &str) -> Node {
51 let mut node = Node::new(Root::new(src.to_owned()));
52 node.srcmap = Some(SourcePos::new(0, src.len()));
53
54 for rule in self.ruler.iter() {
55 rule(&mut node, self);
56 debug_assert!(node.is::<Root>(), "root node of the AST must always be Root");
57 }
58 node
59 }
60
61 pub fn add_rule<T: CoreRule>(&mut self) -> RuleBuilder<RuleFn> {
62 let item = self.ruler.add(TypeKey::of::<T>(), T::run);
63 RuleBuilder::new(item)
64 }
65
66 pub fn has_rule<T: CoreRule>(&mut self) -> bool {
67 self.ruler.contains(TypeKey::of::<T>())
68 }
69
70 pub fn remove_rule<T: CoreRule>(&mut self) {
71 self.ruler.remove(TypeKey::of::<T>());
72 }
73}
74
75impl Default for MarkdownIt {
76 fn default() -> Self {
77 let mut md = Self {
78 block: BlockParser::new(),
79 inline: InlineParser::new(),
80 link_formatter: Box::new(MDLinkFormatter::new()),
81 ext: MarkdownItExtSet::new(),
82 max_nesting: 100,
83 ruler: Ruler::new(),
84 max_indent: i32::MAX,
85 };
86 block::builtin::add(&mut md);
87 inline::builtin::add(&mut md);
88 md
89 }
90}
crates/markdown-it/src/parser/mod.rs created+45
...@@ -0,0 +1,45 @@
1//! Parser itself + stuff that allows you to extend it.
2//!
3//! In order to understand how this parser works, you need to understand the concept
4//! of Rule Chains. "Rule Chain" is an ordered set of functions that get executed
5//! sequentially. This is an example of a Rule Chain:
6//!
7//! ```rust
8//! let rules : Vec<fn(&mut String)> = vec![
9//! |s| { s.push_str("hello"); },
10//! |s| { s.push(','); },
11//! |s| { s.push(' '); },
12//! |s| { s.push_str("world"); },
13//! |s| { s.push('!'); },
14//! ];
15//! dbg!(rules.iter().fold(String::new(), |mut s, f| { f(&mut s); s }));
16//! ```
17//!
18//! The example above builds a string using 5 independent functions. You can extend
19//! it by pushing your own function in that vector that manipulate the state (String)
20//! in any way you like.
21//!
22//! MarkdownIt parser consists of three Rule Chains:
23//! - [inline] (where functions get executed on every character)
24//! - [block] (where functions get executed on every line)
25//! - [core] (where functions get executed once per document)
26//!
27//! You can extend each one of these chains by using
28//! [md.inline.add_rule](inline::InlineParser::add_rule),
29//! [md.block.add_rule](block::BlockParser::add_rule) or
30//! [md.add_rule](crate::MarkdownIt::add_rule) respectively.
31//!
32//! These are examples of the rules in each chain (view source to see implementation):
33//! - [inline rule](crate::plugins::cmark::inline::autolink) - autolink
34//! - [block rule](crate::plugins::cmark::block::hr) - thematic break
35//! - [core rule](crate::plugins::sourcepos) - source mapping
36//!
37pub mod block;
38pub mod core;
39pub mod extset;
40pub mod inline;
41pub mod linkfmt;
42
43pub(super) mod main;
44pub(super) mod node;
45pub(super) mod renderer;
crates/markdown-it/src/parser/node.rs created+229
...@@ -0,0 +1,229 @@
1use downcast_rs::{impl_downcast, Downcast};
2use std::any::TypeId;
3use std::fmt::Debug;
4
5use crate::common::sourcemap::SourcePos;
6use crate::common::TypeKey;
7use crate::parser::extset::NodeExtSet;
8use crate::parser::inline::Text;
9use crate::parser::renderer::HTMLRenderer;
10use crate::plugins::cmark::inline::newline::Softbreak;
11use crate::Renderer;
12
13/// Single node in the CommonMark AST.
14#[derive(Debug)]
15#[readonly::make]
16pub struct Node {
17 /// Array of child nodes.
18 pub children: Vec<Node>,
19
20 /// Source mapping info.
21 pub srcmap: Option<SourcePos>,
22
23 /// Custom data specific to this token.
24 pub ext: NodeExtSet,
25
26 /// Additional attributes to be added to resulting html.
27 pub attrs: Vec<(&'static str, String)>,
28
29 /// Type name, used for debugging.
30 #[readonly]
31 pub node_type: TypeKey,
32
33 /// Storage for arbitrary token-specific data.
34 #[readonly]
35 pub node_value: Box<dyn NodeValue>,
36}
37
38impl Node {
39 /// Create a new [Node](Node) with a custom value.
40 pub fn new<T: NodeValue>(value: T) -> Self {
41 Self {
42 children: Vec::new(),
43 srcmap: None,
44 attrs: Vec::new(),
45 ext: NodeExtSet::new(),
46 node_type: TypeKey::of::<T>(),
47 node_value: Box::new(value),
48 }
49 }
50
51 /// Return std::any::type_name() of node value.
52 pub fn name(&self) -> &'static str {
53 self.node_type.name
54 }
55
56 /// Check that this node value is of given type.
57 pub fn is<T: NodeValue>(&self) -> bool {
58 self.node_type.id == TypeId::of::<T>()
59 }
60
61 /// Downcast node value to specific type.
62 pub fn cast<T: NodeValue>(&self) -> Option<&T> {
63 if self.node_type.id == TypeId::of::<T>() {
64 Some(self.node_value.downcast_ref::<T>().unwrap())
65 // performance note: `node_type.id` improves walk speed by a LOT by removing indirection
66 // (~5% of overall program speed), so having type id duplicated in Node is very beneficial;
67 // we can also remove extra check with downcast_unchecked, but it doesn't do much
68 //Some(unsafe { &*(&*self.node_value as *const dyn NodeValue as *const T) })
69 } else {
70 None
71 }
72 }
73
74 /// Downcast node value to specific type.
75 pub fn cast_mut<T: NodeValue>(&mut self) -> Option<&mut T> {
76 if self.node_type.id == TypeId::of::<T>() {
77 Some(self.node_value.downcast_mut::<T>().unwrap())
78 // performance note: see above
79 //Some(unsafe { &mut *(&mut *self.node_value as *mut dyn NodeValue as *mut T) })
80 } else {
81 None
82 }
83 }
84
85 /// Render this node to HTML.
86 pub fn render(&self) -> String {
87 let mut fmt = HTMLRenderer::<false>::new();
88 fmt.render(self);
89 fmt.into()
90 }
91
92 /// Render this node to XHTML, it adds slash to self-closing tags like this: `<img />`.
93 ///
94 /// This mode exists for compatibility with CommonMark tests.
95 pub fn xrender(&self) -> String {
96 let mut fmt = HTMLRenderer::<true>::new();
97 fmt.render(self);
98 fmt.into()
99 }
100
101 /// Replace custom value with another value (this is roughly equivalent
102 /// to replacing the entire node and copying children and sourcemaps).
103 pub fn replace<T: NodeValue>(&mut self, value: T) {
104 self.node_type = TypeKey::of::<T>();
105 self.node_value = Box::new(value);
106 }
107
108 /// Execute function `f` recursively on every member of AST tree
109 /// (using preorder deep-first search).
110 pub fn walk<'a>(&'a self, mut f: impl FnMut(&'a Node, u32)) {
111 // performance note: this is faster than emulating recursion using vec stack
112 fn walk_recursive<'b>(node: &'b Node, depth: u32, f: &mut impl FnMut(&'b Node, u32)) {
113 f(node, depth);
114 for n in node.children.iter() {
115 stacker::maybe_grow(64*1024, 1024*1024, || {
116 walk_recursive(n, depth + 1, f);
117 });
118 }
119 }
120
121 walk_recursive(self, 0, &mut f);
122 }
123
124 /// Execute function `f` recursively on every member of AST tree
125 /// (using preorder deep-first search).
126 pub fn walk_mut(&mut self, mut f: impl FnMut(&mut Node, u32)) {
127 // performance note: this is faster than emulating recursion using vec stack
128 fn walk_recursive(node: &mut Node, depth: u32, f: &mut impl FnMut(&mut Node, u32)) {
129 f(node, depth);
130 for n in node.children.iter_mut() {
131 stacker::maybe_grow(64*1024, 1024*1024, || {
132 walk_recursive(n, depth + 1, f);
133 });
134 }
135 }
136
137 walk_recursive(self, 0, &mut f);
138 }
139
140 /// Execute function `f` recursively on every member of AST tree
141 /// (using postorder deep-first search).
142 pub fn walk_post(&self, mut f: impl FnMut(&Node, u32)) {
143 fn walk_recursive(node: &Node, depth: u32, f: &mut impl FnMut(&Node, u32)) {
144 for n in node.children.iter() {
145 stacker::maybe_grow(64*1024, 1024*1024, || {
146 walk_recursive(n, depth + 1, f);
147 });
148 }
149 f(node, depth);
150 }
151
152 walk_recursive(self, 0, &mut f);
153 }
154
155 /// Execute function `f` recursively on every member of AST tree
156 /// (using postorder deep-first search).
157 pub fn walk_post_mut(&mut self, mut f: impl FnMut(&mut Node, u32)) {
158 fn walk_recursive(node: &mut Node, depth: u32, f: &mut impl FnMut(&mut Node, u32)) {
159 for n in node.children.iter_mut() {
160 stacker::maybe_grow(64*1024, 1024*1024, || {
161 walk_recursive(n, depth + 1, f);
162 });
163 }
164 f(node, depth);
165 }
166
167 walk_recursive(self, 0, &mut f);
168 }
169
170 /// Walk recursively through child nodes and collect all text nodes
171 /// into a single string.
172 pub fn collect_text(&self) -> String {
173 let mut result = String::new();
174
175 self.walk(|node, _| {
176 if let Some(text) = node.cast::<Text>() {
177 result.push_str(text.content.as_str());
178 } else if node.is::<Softbreak>() {
179 result.push('\n');
180 }
181 });
182
183 result
184 }
185}
186
187impl Drop for Node {
188 fn drop(&mut self) {
189 self.walk_post_mut(|node, _| {
190 drop(std::mem::take(&mut node.children));
191 });
192 }
193}
194
195#[derive(Debug)]
196#[doc(hidden)]
197pub struct NodeEmpty;
198impl NodeValue for NodeEmpty {}
199
200impl Default for Node {
201 /// Create empty Node. Empty node should only be used as placeholder for functions like
202 /// std::mem::take, and it cannot be rendered.
203 fn default() -> Self {
204 Node::new(NodeEmpty)
205 }
206}
207
208/// Contents of the specific AST node.
209pub trait NodeValue : Debug + Downcast {
210 /// Output HTML corresponding to this node using Renderer API.
211 ///
212 /// Example implementation looks like this:
213 /// ```rust
214 /// # const IGNORE : &str = stringify! {
215 /// fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
216 /// fmt.open("div", &[]);
217 /// fmt.contents(&node.children);
218 /// fmt.close("div");
219 /// fmt.cr();
220 /// }
221 /// # };
222 /// ```
223 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
224 let _ = fmt;
225 unimplemented!("{} doesn't implement render", node.name());
226 }
227}
228
229impl_downcast!(NodeValue);
crates/markdown-it/src/parser/renderer.rs created+154
...@@ -0,0 +1,154 @@
1use std::collections::HashMap;
2use std::fmt::Debug;
3
4use crate::common::utils::escape_html;
5use crate::parser::extset::RenderExtSet;
6use crate::Node;
7
8/// Each node outputs its HTML using this API.
9///
10/// Renderer is a struct that walks through AST and collects HTML from each node
11/// into internal buffer.
12pub trait Renderer {
13 /// Write opening html tag with attributes, e.g. `<a href="url">`.
14 fn open(&mut self, tag: &str, attrs: &[(&str, String)]);
15 /// Write closing html tag, e.g. `</a>`.
16 fn close(&mut self, tag: &str);
17 /// Write self-closing html tag with attributes, e.g. `<img src="url"/>`.
18 fn self_close(&mut self, tag: &str, attrs: &[(&str, String)]);
19 /// Loop through child nodes and render each one.
20 fn contents(&mut self, nodes: &[Node]);
21 /// Write line break (`\n`). Default renderer ignores it if last char in the buffer is `\n` already.
22 fn cr(&mut self);
23 /// Write plain text with escaping, `<div>` -> `&lt;div&gt;`.
24 fn text(&mut self, text: &str);
25 /// Write plain text without escaping, `<div>` -> `<div>`.
26 fn text_raw(&mut self, text: &str);
27 /// Extension set to store custom stuff.
28 fn ext(&mut self) -> &mut RenderExtSet;
29}
30
31#[derive(Debug, Default)]
32/// Default HTML/XHTML renderer.
33pub(crate) struct HTMLRenderer<const XHTML: bool> {
34 result: String,
35 ext: RenderExtSet,
36}
37
38impl<const XHTML: bool> HTMLRenderer<XHTML> {
39 pub fn new() -> Self {
40 Self {
41 result: String::new(),
42 ext: RenderExtSet::new(),
43 }
44 }
45
46 pub fn render(&mut self, node: &Node) {
47 node.node_value.render(node, self);
48 }
49
50 fn make_attr(&mut self, name: &str, value: &str) {
51 self.result.push(' ');
52 self.result.push_str(&escape_html(name));
53 self.result.push('=');
54 self.result.push('"');
55 self.result.push_str(&escape_html(value));
56 self.result.push('"');
57 }
58
59 fn make_attrs(&mut self, attrs: &[(&str, String)]) {
60 let mut attr_hash = HashMap::new();
61 let mut attr_order = Vec::with_capacity(attrs.len());
62
63 for (name, value) in attrs {
64 let entry = attr_hash.entry(*name).or_insert(Vec::new());
65 entry.push(value.as_str());
66 attr_order.push(*name);
67 }
68
69 for name in attr_order {
70 let Some(value) = attr_hash.remove(name) else { continue; };
71
72 if name == "class" {
73 self.make_attr(name, &value.join(" "));
74 } else if name == "style" {
75 self.make_attr(name, &value.join(";"));
76 } else {
77 for v in value {
78 self.make_attr(name, v);
79 }
80 }
81 }
82 }
83}
84
85impl<const XHTML: bool> From<HTMLRenderer<XHTML>> for String {
86 fn from(f: HTMLRenderer<XHTML>) -> Self {
87 #[cold]
88 fn replace_null(input: String) -> String {
89 input.replace('\0', "\u{FFFD}")
90 }
91
92 if f.result.contains('\0') {
93 // U+0000 must be replaced with U+FFFD as per commonmark spec,
94 // we do it at the very end in order to avoid messing with byte offsets
95 // for source maps (since "\0".len() != "\u{FFFD}".len())
96 replace_null(f.result)
97 } else {
98 f.result
99 }
100 }
101}
102
103impl<const XHTML: bool> Renderer for HTMLRenderer<XHTML> {
104 fn open(&mut self, tag: &str, attrs: &[(&str, String)]) {
105 self.result.push('<');
106 self.result.push_str(tag);
107 self.make_attrs(attrs);
108 self.result.push('>');
109 }
110
111 fn close(&mut self, tag: &str) {
112 self.result.push('<');
113 self.result.push('/');
114 self.result.push_str(tag);
115 self.result.push('>');
116 }
117
118 fn self_close(&mut self, tag: &str, attrs: &[(&str, String)]) {
119 self.result.push('<');
120 self.result.push_str(tag);
121 self.make_attrs(attrs);
122 if XHTML {
123 self.result.push(' ');
124 self.result.push('/');
125 }
126 self.result.push('>');
127 }
128
129 fn contents(&mut self, nodes: &[Node]) {
130 for node in nodes.iter() {
131 self.render(node);
132 }
133 }
134
135 fn cr(&mut self) {
136 // only push '\n' if last character isn't it
137 match self.result.as_bytes().last() {
138 Some(b'\n') | None => {}
139 Some(_) => self.result.push('\n')
140 }
141 }
142
143 fn text(&mut self, text: &str) {
144 self.result.push_str(&escape_html(text));
145 }
146
147 fn text_raw(&mut self, text: &str) {
148 self.result.push_str(text);
149 }
150
151 fn ext(&mut self) -> &mut RenderExtSet {
152 &mut self.ext
153 }
154}
crates/markdown-it/src/plugins/cmark/block/blockquote.rs created+168
...@@ -0,0 +1,168 @@
1//! Block quotes
2//!
3//! `> looks like this`
4//!
5//! <https://spec.commonmark.org/0.30/#block-quotes>
6use crate::common::utils::find_indent_of;
7use crate::parser::block::{BlockRule, BlockState};
8use crate::{MarkdownIt, Node, NodeValue, Renderer};
9
10#[derive(Debug)]
11pub struct Blockquote;
12
13impl NodeValue for Blockquote {
14 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
15 fmt.cr();
16 fmt.open("blockquote", &node.attrs);
17 fmt.cr();
18 fmt.contents(&node.children);
19 fmt.cr();
20 fmt.close("blockquote");
21 fmt.cr();
22 }
23}
24
25pub fn add(md: &mut MarkdownIt) {
26 md.block.add_rule::<BlockquoteScanner>();
27}
28
29#[doc(hidden)]
30pub struct BlockquoteScanner;
31impl BlockRule for BlockquoteScanner {
32 fn check(state: &mut BlockState) -> Option<()> {
33
34 if state.line_indent(state.line) >= state.md.max_indent { return None; }
35
36 // check the block quote marker
37 let Some('>') = state.get_line(state.line).chars().next() else { return None; };
38
39 Some(())
40 }
41
42 fn run(state: &mut BlockState) -> Option<(Node, usize)> {
43 Self::check(state)?;
44
45 let mut old_line_offsets = Vec::new();
46 let start_line = state.line;
47 let mut next_line = state.line;
48 let mut last_line_empty = false;
49
50 // Search the end of the block
51 //
52 // Block ends with either:
53 // 1. an empty line outside:
54 // ```
55 // > test
56 //
57 // ```
58 // 2. an empty line inside:
59 // ```
60 // >
61 // test
62 // ```
63 // 3. another tag:
64 // ```
65 // > test
66 // - - -
67 // ```
68 while next_line < state.line_max {
69 // check if it's outdented, i.e. it's inside list item and indented
70 // less than said list item:
71 //
72 // ```
73 // 1. anything
74 // > current blockquote
75 // 2. checking this line
76 // ```
77 let is_outdented = state.line_indent(next_line) < 0;
78 let line = state.get_line(next_line).to_owned();
79 let mut chars = line.chars();
80
81 match chars.next() {
82 None => {
83 // Case 1: line is not inside the blockquote, and this line is empty.
84 break;
85 }
86 Some('>') if !is_outdented => {
87 // This line is inside the blockquote.
88
89 // set offset past spaces and ">"
90 let offsets = &state.line_offsets[next_line];
91 let pos_after_marker = offsets.first_nonspace + 1;
92
93 old_line_offsets.push(state.line_offsets[next_line].clone());
94
95 let ( mut indent_after_marker, first_nonspace ) = find_indent_of(
96 &state.src[offsets.line_start..offsets.line_end],
97 pos_after_marker - offsets.line_start);
98
99 last_line_empty = first_nonspace == offsets.line_end - offsets.line_start;
100
101 // skip one optional space after '>'
102 if matches!(chars.next(), Some(' ' | '\t')) {
103 indent_after_marker -= 1;
104 }
105
106 state.line_offsets[next_line].indent_nonspace = indent_after_marker as i32;
107 state.line_offsets[next_line].first_nonspace = first_nonspace + state.line_offsets[next_line].line_start;
108 next_line += 1;
109 continue;
110 }
111 _ => {}
112 }
113
114 // Case 2: line is not inside the blockquote, and the last line was empty.
115 if last_line_empty { break; }
116
117 // Case 3: another tag found.
118 state.line = next_line;
119
120 if state.test_rules_at_line() {
121 // Quirk to enforce "hard termination mode" for paragraphs;
122 // normally if you call `nodeize(state, startLine, nextLine)`,
123 // paragraphs will look below nextLine for paragraph continuation,
124 // but if blockquote is terminated by another tag, they shouldn't
125 //state.line_max = next_line;
126
127 if state.blk_indent != 0 {
128 // state.blkIndent was non-zero, we now set it to zero,
129 // so we need to re-calculate all offsets to appear as
130 // if indent wasn't changed
131 old_line_offsets.push(state.line_offsets[next_line].clone());
132 state.line_offsets[next_line].indent_nonspace -= state.blk_indent as i32;
133 }
134
135 break;
136 }
137
138 old_line_offsets.push(state.line_offsets[next_line].clone());
139
140 // A negative indentation means that this is a paragraph continuation
141 //
142 state.line_offsets[next_line].indent_nonspace = -1;
143 next_line += 1;
144 }
145
146 let old_indent = state.blk_indent;
147 state.blk_indent = 0;
148
149 let old_node = std::mem::replace(&mut state.node, Node::new(Blockquote));
150 let old_line_max = state.line_max;
151 state.line = start_line;
152 state.line_max = next_line;
153 state.md.block.tokenize(state);
154 next_line = state.line;
155 state.line = start_line;
156 state.line_max = old_line_max;
157
158 // Restore original tShift; this might not be necessary since the parser
159 // has already been here, but just to make sure we can do that.
160 for (idx, line_offset) in old_line_offsets.iter_mut().enumerate() {
161 std::mem::swap(&mut state.line_offsets[idx + start_line], line_offset);
162 }
163 state.blk_indent = old_indent;
164
165 let node = std::mem::replace(&mut state.node, old_node);
166 Some((node, next_line - start_line))
167 }
168}
crates/markdown-it/src/plugins/cmark/block/code.rs created+69
...@@ -0,0 +1,69 @@
1//! Indented code block
2//!
3//! Parses anything indented with 4 spaces.
4//!
5//! <https://spec.commonmark.org/0.30/#indented-code-block>
6use crate::parser::block::{BlockRule, BlockState};
7use crate::{MarkdownIt, Node, NodeValue, Renderer};
8
9const CODE_INDENT: i32 = 4;
10
11#[derive(Debug)]
12pub struct CodeBlock {
13 pub content: String,
14}
15
16impl NodeValue for CodeBlock {
17 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
18 fmt.cr();
19 fmt.open("pre", &[]);
20 fmt.open("code", &node.attrs);
21 fmt.text(&self.content);
22 fmt.close("code");
23 fmt.close("pre");
24 fmt.cr();
25 }
26}
27
28pub fn add(md: &mut MarkdownIt) {
29 md.block.add_rule::<CodeScanner>();
30 md.max_indent = CODE_INDENT;
31}
32
33#[doc(hidden)]
34pub struct CodeScanner;
35impl BlockRule for CodeScanner {
36 fn check(_: &mut BlockState) -> Option<()> {
37 None
38 }
39
40 fn run(state: &mut BlockState) -> Option<(Node, usize)> {
41 if state.line_indent(state.line) < CODE_INDENT { return None; }
42
43 let mut next_line = state.line + 1;
44 let mut last = next_line;
45
46 while next_line < state.line_max {
47 if state.is_empty(next_line) {
48 next_line += 1;
49 continue;
50 }
51
52 if state.line_indent(next_line) >= CODE_INDENT {
53 next_line += 1;
54 last = next_line;
55 continue;
56 }
57
58 break;
59 }
60
61 let (mut content, _mapping) = state.get_lines(state.line, last, CODE_INDENT as usize + state.blk_indent, false);
62 content += "\n";
63
64 let node = Node::new(CodeBlock { content });
65 //node.srcmap = state.get_map_from_offsets(mapping[0].1, state.line_offsets[last - 1].line_end);
66
67 Some((node, last - state.line))
68 }
69}
crates/markdown-it/src/plugins/cmark/block/fence.rs created+164
...@@ -0,0 +1,164 @@
1//! Code fence
2//!
3//! ` ```lang ` or `~~~lang`
4//!
5//! <https://spec.commonmark.org/0.30/#code-fence>
6use crate::common::utils::unescape_all;
7use crate::parser::block::{BlockRule, BlockState};
8use crate::parser::extset::MarkdownItExt;
9use crate::{MarkdownIt, Node, NodeValue, Renderer};
10
11#[derive(Debug)]
12pub struct CodeFence {
13 pub info: String,
14 pub marker: char,
15 pub marker_len: usize,
16 pub content: String,
17 pub lang_prefix: &'static str,
18}
19
20impl NodeValue for CodeFence {
21 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
22 let info = unescape_all(&self.info);
23 let mut split = info.split_whitespace();
24 let lang_name = split.next().unwrap_or("");
25 let mut attrs = node.attrs.clone();
26 let class;
27
28 if !lang_name.is_empty() {
29 class = format!("{}{}", self.lang_prefix, lang_name);
30 attrs.push(("class", class));
31 }
32
33 fmt.cr();
34 fmt.open("pre", &[]);
35 fmt.open("code", &attrs);
36 fmt.text(&self.content);
37 fmt.close("code");
38 fmt.close("pre");
39 fmt.cr();
40 }
41}
42
43#[derive(Debug, Clone, Copy)]
44struct FenceSettings(&'static str);
45impl MarkdownItExt for FenceSettings {}
46
47impl Default for FenceSettings {
48 fn default() -> Self {
49 Self("language-")
50 }
51}
52
53pub fn add(md: &mut MarkdownIt) {
54 md.block.add_rule::<FenceScanner>();
55}
56
57pub fn set_lang_prefix(md: &mut MarkdownIt, lang_prefix: &'static str) {
58 md.ext.insert(FenceSettings(lang_prefix));
59}
60
61#[doc(hidden)]
62pub struct FenceScanner;
63
64impl FenceScanner {
65 fn get_header<'a>(state: &'a mut BlockState) -> Option<(char, usize, &'a str)> {
66
67 if state.line_indent(state.line) >= state.md.max_indent { return None; }
68
69 let line = state.get_line(state.line);
70 let mut chars = line.chars();
71
72 let marker = chars.next()?;
73 if marker != '~' && marker != '`' { return None; }
74
75 // scan marker length
76 let mut len = 1;
77 while Some(marker) == chars.next() { len += 1; }
78
79 if len < 3 { return None; }
80
81 let params = &line[len..];
82
83 if marker == '`' && params.contains(marker) { return None; }
84
85 Some((marker, len, params))
86 }
87}
88
89impl BlockRule for FenceScanner {
90 fn check(state: &mut BlockState) -> Option<()> {
91 Self::get_header(state).map(|_| ())
92 }
93
94 fn run(state: &mut BlockState) -> Option<(Node, usize)> {
95 let (marker, len, params) = Self::get_header(state)?;
96 let params = params.to_owned();
97
98 let mut next_line = state.line;
99 let mut have_end_marker = false;
100
101 // search end of block
102 'outer: loop {
103 next_line += 1;
104 if next_line >= state.line_max {
105 // unclosed block should be autoclosed by end of document.
106 // also block seems to be autoclosed by end of parent
107 break;
108 }
109
110 let line = state.get_line(next_line);
111
112 if !line.is_empty() && state.line_indent(next_line) < 0 {
113 // non-empty line with negative indent should stop the list:
114 // - ```
115 // test
116 break;
117 }
118
119 let mut chars = line.chars().peekable();
120
121 if Some(marker) != chars.next() { continue; }
122
123 if state.line_indent(next_line) >= state.md.max_indent {
124 continue;
125 }
126
127 // scan marker length
128 let mut len_end = 1;
129 while Some(&marker) == chars.peek() {
130 chars.next();
131 len_end += 1;
132 }
133
134 // closing code fence must be at least as long as the opening one
135 if len_end < len { continue; }
136
137 // make sure tail has spaces only
138 loop {
139 match chars.next() {
140 Some(' ' | '\t') => {},
141 Some(_) => continue 'outer,
142 None => {
143 have_end_marker = true;
144 break 'outer;
145 }
146 }
147 }
148 }
149
150 // If a fence has heading spaces, they should be removed from its inner block
151 let indent = state.line_offsets[state.line].indent_nonspace;
152 let (content, _) = state.get_lines(state.line + 1, next_line, indent as usize, true);
153
154 let lang_prefix = state.md.ext.get::<FenceSettings>().copied().unwrap_or_default().0;
155 let node = Node::new(CodeFence {
156 info: params,
157 marker,
158 marker_len: len,
159 content,
160 lang_prefix,
161 });
162 Some((node, next_line - state.line + if have_end_marker { 1 } else { 0 }))
163 }
164}
crates/markdown-it/src/plugins/cmark/block/heading.rs created+87
...@@ -0,0 +1,87 @@
1//! ATX heading
2//!
3//! `# h1`, `## h2`, etc.
4//!
5//! <https://spec.commonmark.org/0.30/#atx-heading>
6use crate::parser::block::{BlockRule, BlockState};
7use crate::parser::inline::InlineRoot;
8use crate::{MarkdownIt, Node, NodeValue, Renderer};
9
10#[derive(Debug)]
11pub struct ATXHeading {
12 pub level: u8,
13}
14
15impl NodeValue for ATXHeading {
16 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
17 static TAG : [&str; 6] = [ "h1", "h2", "h3", "h4", "h5", "h6" ];
18 debug_assert!(self.level >= 1 && self.level <= 6);
19
20 fmt.cr();
21 fmt.open(TAG[self.level as usize - 1], &node.attrs);
22 fmt.contents(&node.children);
23 fmt.close(TAG[self.level as usize - 1]);
24 fmt.cr();
25 }
26}
27
28pub fn add(md: &mut MarkdownIt) {
29 md.block.add_rule::<HeadingScanner>();
30}
31
32#[doc(hidden)]
33pub struct HeadingScanner;
34impl BlockRule for HeadingScanner {
35 fn run(state: &mut BlockState) -> Option<(Node, usize)> {
36
37 if state.line_indent(state.line) >= state.md.max_indent { return None; }
38
39 let line = state.get_line(state.line);
40 let Some('#') = line.chars().next() else { return None; };
41
42 let text_pos;
43
44 // count heading level
45 let mut level = 0u8;
46 let mut chars = line.char_indices();
47 loop {
48 match chars.next() {
49 Some((_, '#')) => {
50 level += 1;
51 if level > 6 { return None; }
52 }
53 Some((x, ' ' | '\t')) => {
54 text_pos = x;
55 break;
56 }
57 None => {
58 text_pos = level as usize;
59 break;
60 }
61 Some(_) => return None,
62 }
63 }
64
65 // Let's cut tails like ' ### ' from the end of string
66
67 let mut chars_back = chars.rev().peekable();
68 while let Some((_, ' ' | '\t')) = chars_back.peek() { chars_back.next(); }
69 while let Some((_, '#')) = chars_back.peek() { chars_back.next(); }
70
71 let text_max = match chars_back.next() {
72 // ## foo ##
73 Some((last_pos, ' ' | '\t')) => last_pos + 1,
74 // ## foo##
75 Some(_) => line.len(),
76 // ## ## (already consumed the space)
77 None => text_pos,
78 };
79
80 let content = line[text_pos..text_max].to_owned();
81 let mapping = vec![(0, state.line_offsets[state.line].first_nonspace + text_pos)];
82
83 let mut node = Node::new(ATXHeading { level });
84 node.children.push(Node::new(InlineRoot::new(content, mapping)));
85 Some((node, 1))
86 }
87}
crates/markdown-it/src/plugins/cmark/block/hr.rs created+55
...@@ -0,0 +1,55 @@
1//! Thematic breaks
2//!
3//! `***`, `---`, `___`
4//!
5//! <https://spec.commonmark.org/0.30/#thematic-breaks>
6use crate::parser::block::{BlockRule, BlockState};
7use crate::{MarkdownIt, Node, NodeValue, Renderer};
8
9#[derive(Debug)]
10pub struct ThematicBreak {
11 pub marker: char,
12 pub marker_len: usize,
13}
14
15impl NodeValue for ThematicBreak {
16 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
17 fmt.cr();
18 fmt.self_close("hr", &node.attrs);
19 fmt.cr();
20 }
21}
22
23pub fn add(md: &mut MarkdownIt) {
24 md.block.add_rule::<HrScanner>();
25}
26
27#[doc(hidden)]
28pub struct HrScanner;
29impl BlockRule for HrScanner {
30 fn run(state: &mut BlockState) -> Option<(Node, usize)> {
31
32 if state.line_indent(state.line) >= state.md.max_indent { return None; }
33
34 let mut chars = state.get_line(state.line).chars();
35
36 // Check hr marker
37 let marker = chars.next()?;
38 if marker != '*' && marker != '-' && marker != '_' { return None; }
39
40 // markers can be mixed with spaces, but there should be at least 3 of them
41 let mut cnt = 1;
42 for ch in chars {
43 if ch == marker {
44 cnt += 1;
45 } else if ch != ' ' && ch != '\t' {
46 return None;
47 }
48 }
49
50 if cnt < 3 { return None; }
51
52 let node = Node::new(ThematicBreak { marker, marker_len: cnt });
53 Some((node, 1))
54 }
55}
crates/markdown-it/src/plugins/cmark/block/lheading.rs created+104
...@@ -0,0 +1,104 @@
1//! Setext headings
2//!
3//! Paragraph underlined with `===` or `---`.
4//!
5//! <https://spec.commonmark.org/0.30/#setext-headings>
6use crate::parser::block::{BlockRule, BlockState};
7use crate::parser::inline::InlineRoot;
8use crate::plugins::cmark::block::paragraph::ParagraphScanner;
9use crate::{MarkdownIt, Node, NodeValue, Renderer};
10
11#[derive(Debug)]
12pub struct SetextHeader {
13 pub level: u8,
14 pub marker: char,
15}
16
17impl NodeValue for SetextHeader {
18 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
19 static TAG : [&str; 2] = [ "h1", "h2" ];
20 debug_assert!(self.level >= 1 && self.level <= 2);
21
22 fmt.cr();
23 fmt.open(TAG[self.level as usize - 1], &node.attrs);
24 fmt.contents(&node.children);
25 fmt.close(TAG[self.level as usize - 1]);
26 fmt.cr();
27 }
28}
29
30pub fn add(md: &mut MarkdownIt) {
31 md.block.add_rule::<LHeadingScanner>()
32 .before::<ParagraphScanner>()
33 .after_all();
34}
35
36#[doc(hidden)]
37pub struct LHeadingScanner;
38impl BlockRule for LHeadingScanner {
39 fn check(_: &mut BlockState) -> Option<()> {
40 None // can't interrupt any tags
41 }
42
43 fn run(state: &mut BlockState) -> Option<(Node, usize)> {
44
45 if state.line_indent(state.line) >= state.md.max_indent { return None; }
46
47 let start_line = state.line;
48 let mut next_line = start_line;
49 let mut level = 0;
50
51 'outer: loop {
52 next_line += 1;
53
54 if next_line >= state.line_max || state.is_empty(next_line) { break; }
55
56 // this may be a code block normally, but after paragraph
57 // it's considered a lazy continuation regardless of what's there
58 if state.line_indent(next_line) >= state.md.max_indent { continue; }
59
60 //
61 // Check for underline in setext header
62 //
63 if state.line_indent(next_line) >= 0 {
64 let mut chars = state.get_line(next_line).chars().peekable();
65 if let Some(marker @ ('-' | '=')) = chars.next() {
66 while Some(&marker) == chars.peek() { chars.next(); }
67 while let Some(' ' | '\t') = chars.peek() { chars.next(); }
68 if chars.next().is_none() {
69 level = if marker == '=' { 1 } else { 2 };
70 break 'outer;
71 }
72 }
73 }
74
75 // quirk for blockquotes, this line should already be checked by that rule
76 if state.line_offsets[next_line].indent_nonspace < 0 { continue; }
77
78 // Some tags can terminate paragraph without empty line.
79 let old_state_line = state.line;
80 state.line = next_line;
81 if state.test_rules_at_line() {
82 state.line = old_state_line;
83 break 'outer;
84 }
85 state.line = old_state_line;
86 }
87
88
89 if level == 0 {
90 // Didn't find valid underline
91 return None;
92 }
93
94 let (content, mapping) = state.get_lines(start_line, next_line, state.blk_indent, false);
95
96 let mut node = Node::new(SetextHeader {
97 level,
98 marker: if level == 2 { '-' } else { '=' }
99 });
100 node.children.push(Node::new(InlineRoot::new(content, mapping)));
101
102 Some((node, next_line + 1 - start_line))
103 }
104}
crates/markdown-it/src/plugins/cmark/block/list.rs created+370
...@@ -0,0 +1,370 @@
1//! Ordered and bullet lists
2//!
3//! This plugin parses both kinds of lists (bullet and ordered) as well as list items.
4//!
5//! looks like `1. this` or `- this`
6//!
7//! - <https://spec.commonmark.org/0.30/#lists>
8//! - <https://spec.commonmark.org/0.30/#list-items>
9use crate::common::utils::find_indent_of;
10use crate::parser::block::{BlockRule, BlockState};
11use crate::plugins::cmark::block::hr::HrScanner;
12use crate::plugins::cmark::block::paragraph::Paragraph;
13use crate::{MarkdownIt, Node, NodeValue, Renderer};
14
15#[derive(Debug)]
16pub struct OrderedList {
17 pub start: u32,
18 pub marker: char,
19}
20
21impl NodeValue for OrderedList {
22 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
23 let mut attrs = node.attrs.clone();
24 let start;
25 if self.start != 1 {
26 start = self.start.to_string();
27 attrs.push(("start", start));
28 }
29 fmt.cr();
30 fmt.open("ol", &attrs);
31 fmt.cr();
32 fmt.contents(&node.children);
33 fmt.cr();
34 fmt.close("ol");
35 fmt.cr();
36 }
37}
38
39#[derive(Debug)]
40pub struct BulletList {
41 pub marker: char,
42}
43
44impl NodeValue for BulletList {
45 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
46 fmt.cr();
47 fmt.open("ul", &node.attrs);
48 fmt.cr();
49 fmt.contents(&node.children);
50 fmt.cr();
51 fmt.close("ul");
52 fmt.cr();
53 }
54}
55
56#[derive(Debug)]
57pub struct ListItem;
58
59impl NodeValue for ListItem {
60 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
61 fmt.open("li", &node.attrs);
62 fmt.contents(&node.children);
63 fmt.close("li");
64 fmt.cr();
65 }
66}
67
68pub fn add(md: &mut MarkdownIt) {
69 md.block.add_rule::<ListScanner>()
70 .after::<HrScanner>();
71}
72
73#[doc(hidden)]
74pub struct ListScanner;
75
76impl ListScanner {
77 // Search `[-+*][\n ]`, returns next pos after marker on success
78 // or -1 on fail.
79 fn skip_bullet_list_marker(src: &str) -> Option<usize> {
80 let mut chars = src.chars();
81
82 let Some('*' | '-' | '+') = chars.next() else { return None; };
83
84 match chars.next() {
85 Some(' ' | '\t') | None => Some(1),
86 Some(_) => None, // " -test " - is not a list item
87 }
88 }
89
90 // Search `\d+[.)][\n ]`, returns next pos after marker on success
91 // or -1 on fail.
92 fn skip_ordered_list_marker(src: &str) -> Option<usize> {
93 let mut chars = src.chars();
94 let Some('0'..='9') = chars.next() else { return None; };
95
96 let mut pos = 1;
97 loop {
98 pos += 1;
99 match chars.next() {
100 Some('0'..='9') => {
101 // List marker should have no more than 9 digits
102 // (prevents integer overflow in browsers)
103 if pos >= 10 { return None; }
104 }
105 Some(')' | '.') => {
106 // found valid marker
107 break;
108 }
109 Some(_) | None => { return None; }
110 }
111 }
112
113 match chars.next() {
114 Some(' ' | '\t') | None => Some(pos),
115 Some(_) => None, // " 1.test " - is not a list item
116 }
117 }
118
119 fn mark_tight_paragraphs(nodes: &mut Vec<Node>) {
120 let mut idx = 0;
121 while idx < nodes.len() {
122 if nodes[idx].is::<Paragraph>() {
123 let children = std::mem::take(&mut nodes[idx].children);
124 let len = children.len();
125 nodes.splice(idx..idx+1, children);
126 idx += len;
127 } else {
128 idx += 1;
129 }
130 }
131 }
132
133 fn find_marker(state: &mut BlockState, silent: bool) -> Option<(usize, Option<u32>, char)> {
134
135 if state.line_indent(state.line) >= state.md.max_indent { return None; }
136
137 // Special case:
138 // - item 1
139 // - item 2
140 // - item 3
141 // - item 4
142 // - this one is a paragraph continuation
143 if let Some(list_indent) = state.list_indent {
144 let indent_nonspace = state.line_offsets[state.line].indent_nonspace;
145 if indent_nonspace - list_indent as i32 >= state.md.max_indent &&
146 indent_nonspace < state.blk_indent as i32 {
147 return None;
148 }
149 }
150
151 let mut is_terminating_paragraph = false;
152
153 // limit conditions when list can interrupt
154 // a paragraph (validation mode only)
155 if silent {
156 // Next list item should still terminate previous list item;
157 //
158 // This code can fail if plugins use blkIndent as well as lists,
159 // but I hope the spec gets fixed long before that happens.
160 //
161 if state.line_indent(state.line) >= 0 {
162 is_terminating_paragraph = true;
163 }
164 }
165
166 let current_line = state.get_line(state.line);
167
168 let marker_value;
169 let pos_after_marker;
170
171 // Detect list type and position after marker
172 if let Some(p) = Self::skip_ordered_list_marker(current_line) {
173 pos_after_marker = p;
174 let int = str::parse(&current_line[..pos_after_marker - 1]).unwrap();
175 marker_value = Some(int);
176
177 // If we're starting a new ordered list right after
178 // a paragraph, it should start with 1.
179 if is_terminating_paragraph && int != 1 { return None; }
180
181 } else if let Some(p) = Self::skip_bullet_list_marker(current_line) {
182 pos_after_marker = p;
183 marker_value = None;
184 } else {
185 return None;
186 }
187
188 // If we're starting a new unordered list right after
189 // a paragraph, first line should not be empty.
190 if is_terminating_paragraph {
191 let mut chars = current_line[pos_after_marker..].chars();
192 loop {
193 match chars.next() {
194 Some(' ' | '\t') => {},
195 Some(_) => break,
196 None => return None,
197 }
198 }
199 }
200
201 // We should terminate list on style change. Remember first one to compare.
202 let marker_char = current_line[..pos_after_marker].chars().next_back().unwrap();
203
204 Some((pos_after_marker, marker_value, marker_char))
205 }
206}
207
208impl BlockRule for ListScanner {
209 fn check(state: &mut BlockState) -> Option<()> {
210 if state.node.is::<BulletList>() || state.node.is::<OrderedList>() { return None; }
211
212 Self::find_marker(state, true).map(|_| ())
213 }
214
215 fn run(state: &mut BlockState) -> Option<(Node, usize)> {
216 let (mut pos_after_marker, marker_value, marker_char) = Self::find_marker(state, false)?;
217
218 let new_node = if let Some(int) = marker_value {
219 Node::new(OrderedList {
220 start: int,
221 marker: marker_char
222 })
223 } else {
224 Node::new(BulletList {
225 marker: marker_char
226 })
227 };
228
229 let old_node = std::mem::replace(&mut state.node, new_node);
230
231 //
232 // Iterate list items
233 //
234
235 let start_line = state.line;
236 let mut next_line = state.line;
237 let mut prev_empty_end = false;
238 let mut tight = true;
239 let mut current_line;
240
241 while next_line < state.line_max {
242 let offsets = &state.line_offsets[next_line];
243 let initial = offsets.indent_nonspace as usize + pos_after_marker;
244
245 let ( mut indent_after_marker, first_nonspace ) = find_indent_of(
246 &state.src[offsets.line_start..offsets.line_end],
247 pos_after_marker + offsets.first_nonspace - offsets.line_start);
248
249 let reached_end_of_line = first_nonspace == offsets.line_end - offsets.line_start;
250 let indent_nonspace = initial + indent_after_marker;
251
252 #[allow(clippy::if_same_then_else)]
253 if reached_end_of_line {
254 // trimming space in "- \n 3" case, indent is 1 here
255 indent_after_marker = 1;
256 } else if indent_after_marker as i32 > state.md.max_indent {
257 // If we have more than the max indent, the indent is 1
258 // (the rest is just indented code block)
259 indent_after_marker = 1;
260 }
261
262 // " - test"
263 // ^^^^^ - calculating total length of this thing
264 let indent = initial + indent_after_marker;
265
266 // Run subparser & write tokens
267 let old_node = std::mem::replace(&mut state.node, Node::new(ListItem));
268
269 // change current state, then restore it after parser subcall
270 let old_tight = state.tight;
271 let old_lineoffset = offsets.clone();
272
273 // - example list
274 // ^ listIndent position will be here
275 // ^ blkIndent position will be here
276 //
277 let old_list_indent = state.list_indent;
278 state.list_indent = Some(state.blk_indent as u32);
279 state.blk_indent = indent;
280
281 state.tight = true;
282 state.line_offsets[next_line].first_nonspace = first_nonspace + state.line_offsets[next_line].line_start;
283 state.line_offsets[next_line].indent_nonspace = indent_nonspace as i32;
284
285 if reached_end_of_line && state.is_empty(next_line + 1) {
286 // workaround for this case
287 // (list item is empty, list terminates before "foo"):
288 // ~~~~~~~~
289 // -
290 //
291 // foo
292 // ~~~~~~~~
293 state.line = if state.line + 2 < state.line_max {
294 state.line + 2
295 } else {
296 state.line_max
297 }
298 } else {
299 state.line = next_line;
300 state.md.block.tokenize(state);
301 }
302
303 // If any of list item is tight, mark list as tight
304 if !state.tight || prev_empty_end {
305 tight = false;
306 }
307
308 // Item become loose if finish with empty line,
309 // but we should filter last element, because it means list finish
310 prev_empty_end = (state.line - next_line) > 1 && state.is_empty(state.line - 1);
311
312 state.blk_indent = state.list_indent.unwrap() as usize;
313 state.list_indent = old_list_indent;
314 state.line_offsets[next_line] = old_lineoffset;
315 state.tight = old_tight;
316
317 let end_line = state.line;
318 let mut node = std::mem::replace(&mut state.node, old_node);
319 node.srcmap = state.get_map(next_line, end_line - 1);
320 state.node.children.push(node);
321 next_line = state.line;
322
323 if next_line >= state.line_max { break; }
324
325 //
326 // Try to check if list is terminated or continued.
327 //
328 if state.line_indent(next_line) < 0 { break; }
329
330 if state.line_indent(next_line) >= state.md.max_indent { break; }
331
332 // fail if terminating block found
333 if state.test_rules_at_line() { break; }
334
335 current_line = state.get_line(state.line).to_owned();
336
337 // fail if list has another type
338 #[allow(clippy::collapsible_else_if)]
339 if marker_value.is_some() {
340 if let Some(p) = Self::skip_ordered_list_marker(&current_line) {
341 pos_after_marker = p;
342 } else {
343 break;
344 }
345 } else {
346 if let Some(p) = Self::skip_bullet_list_marker(&current_line) {
347 pos_after_marker = p;
348 } else {
349 break;
350 }
351 }
352
353 let next_marker_char = current_line[..pos_after_marker].chars().next_back().unwrap();
354 if next_marker_char != marker_char { break; }
355 }
356
357 // mark paragraphs tight if needed
358 if tight {
359 for child in state.node.children.iter_mut() {
360 debug_assert!(child.is::<ListItem>());
361 Self::mark_tight_paragraphs(&mut child.children);
362 }
363 }
364
365 // Finalize list
366 state.line = start_line;
367 let node = std::mem::replace(&mut state.node, old_node);
368 Some((node, next_line - state.line))
369 }
370}
crates/markdown-it/src/plugins/cmark/block/mod.rs created+10
...@@ -0,0 +1,10 @@
1//! Block-level CommonMark structures.
2pub mod blockquote;
3pub mod code;
4pub mod fence;
5pub mod heading;
6pub mod hr;
7pub mod lheading;
8pub mod list;
9pub mod paragraph;
10pub mod reference;
crates/markdown-it/src/plugins/cmark/block/paragraph.rs created+68
...@@ -0,0 +1,68 @@
1//! Paragraph
2//!
3//! This is the default rule if nothing else matches.
4//!
5//! <https://spec.commonmark.org/0.30/#paragraph>
6use crate::parser::block::{BlockRule, BlockState};
7use crate::parser::inline::InlineRoot;
8use crate::{MarkdownIt, Node, NodeValue, Renderer};
9
10pub fn add(md: &mut MarkdownIt) {
11 md.block.add_rule::<ParagraphScanner>()
12 .after_all();
13}
14
15#[derive(Debug)]
16pub struct Paragraph;
17
18impl NodeValue for Paragraph {
19 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
20 fmt.cr();
21 fmt.open("p", &node.attrs);
22 fmt.contents(&node.children);
23 fmt.close("p");
24 fmt.cr();
25 }
26}
27
28#[doc(hidden)]
29pub struct ParagraphScanner;
30impl BlockRule for ParagraphScanner {
31 fn check(_: &mut BlockState) -> Option<()> {
32 None // can't interrupt anything
33 }
34
35 fn run(state: &mut BlockState) -> Option<(Node, usize)> {
36 let start_line = state.line;
37 let mut next_line = start_line;
38
39 // jump line-by-line until empty one or EOF
40 'outer: loop {
41 next_line += 1;
42
43 if next_line >= state.line_max || state.is_empty(next_line) { break; }
44
45 // this may be a code block normally, but after paragraph
46 // it's considered a lazy continuation regardless of what's there
47 if state.line_indent(next_line) >= state.md.max_indent { continue; }
48
49 // quirk for blockquotes, this line should already be checked by that rule
50 if state.line_offsets[next_line].indent_nonspace < 0 { continue; }
51
52 // Some tags can terminate paragraph without empty line.
53 let old_state_line = state.line;
54 state.line = next_line;
55 if state.test_rules_at_line() {
56 state.line = old_state_line;
57 break 'outer;
58 }
59 state.line = old_state_line;
60 }
61
62 let (content, mapping) = state.get_lines(start_line, next_line, state.blk_indent, false);
63
64 let mut node = Node::new(Paragraph);
65 node.children.push(Node::new(InlineRoot::new(content, mapping)));
66 Some((node, next_line - start_line))
67 }
68}
crates/markdown-it/src/plugins/cmark/block/reference.rs created+374
...@@ -0,0 +1,374 @@
1//! Link reference definition
2//!
3//! `[label]: /url "title"`
4//!
5//! <https://spec.commonmark.org/0.30/#link-reference-definition>
6//!
7//! This plugin parses markdown link references. Check documentation on [ReferenceMap]
8//! to see how you can use and/or extend it if you have external source for references.
9//!
10use derivative::Derivative;
11use derive_more::{Deref, DerefMut};
12use downcast_rs::{impl_downcast, Downcast};
13use std::collections::HashMap;
14use std::fmt::Debug;
15
16use crate::common::utils::normalize_reference;
17use crate::generics::inline::full_link;
18use crate::parser::block::{BlockRule, BlockState};
19use crate::parser::extset::RootExt;
20use crate::{MarkdownIt, Node, NodeValue};
21
22/// Storage for parsed references
23///
24/// if you have some external source for your link references, you can add them like this:
25///
26/// ```rust
27/// use markdown_it::parser::block::builtin::BlockParserRule;
28/// use markdown_it::parser::core::{CoreRule, Root};
29/// use markdown_it::plugins::cmark::block::reference::{ReferenceMap, DefaultReferenceMap, CustomReferenceMap};
30/// use markdown_it::{MarkdownIt, Node};
31///
32/// let md = &mut MarkdownIt::new();
33/// markdown_it::plugins::cmark::add(md);
34///
35/// #[derive(Debug, Default)]
36/// struct RefMapOverride(DefaultReferenceMap);
37/// impl CustomReferenceMap for RefMapOverride {
38/// fn get(&self, label: &str) -> Option<(&str, Option<&str>)> {
39/// // override a specific link
40/// if label == "rust" {
41/// return Some((
42/// "https://www.rust-lang.org/",
43/// Some("The Rust Language"),
44/// ));
45/// }
46///
47/// self.0.get(label)
48/// }
49///
50/// fn insert(&mut self, label: String, destination: String, title: Option<String>) -> bool {
51/// self.0.insert(label, destination, title)
52/// }
53/// }
54///
55/// struct AddCustomReferences;
56/// impl CoreRule for AddCustomReferences {
57/// fn run(root: &mut Node, _: &MarkdownIt) {
58/// let data = root.cast_mut::<Root>().unwrap();
59/// data.ext.insert(ReferenceMap::new(RefMapOverride::default()));
60/// }
61/// }
62///
63/// md.add_rule::<AddCustomReferences>()
64/// .before::<BlockParserRule>();
65///
66/// let html = md.parse("[rust]").render();
67/// assert_eq!(
68/// html.trim(),
69/// r#"<p><a href="https://www.rust-lang.org/" title="The Rust Language">rust</a></p>"#
70/// );
71/// ```
72///
73/// You can also view all references that user created by adding the following rule:
74///
75/// ```rust
76/// use markdown_it::parser::core::{CoreRule, Root};
77/// use markdown_it::plugins::cmark::block::reference::{ReferenceMap, DefaultReferenceMap};
78/// use markdown_it::{MarkdownIt, Node};
79///
80/// let md = &mut MarkdownIt::new();
81/// markdown_it::plugins::cmark::add(md);
82///
83/// let ast = md.parse("[hello]: world");
84/// let root = ast.node_value.downcast_ref::<Root>().unwrap();
85/// let refmap = root.ext.get::<ReferenceMap>()
86/// .map(|m| m.downcast_ref::<DefaultReferenceMap>().expect("expect references to be handled by default map"));
87///
88/// let mut labels = vec![];
89/// if let Some(refmap) = refmap {
90/// for (label, _dest, _title) in refmap.iter() {
91/// labels.push(label);
92/// }
93/// }
94///
95/// assert_eq!(labels, ["hello"]);
96/// ```
97///
98#[derive(Debug, Deref, DerefMut)]
99#[deref(forward)]
100#[deref_mut(forward)]
101pub struct ReferenceMap(Box<dyn CustomReferenceMap>);
102
103impl ReferenceMap {
104 pub fn new(custom_map: impl CustomReferenceMap + 'static) -> Self {
105 Self(Box::new(custom_map))
106 }
107}
108
109impl Default for ReferenceMap {
110 fn default() -> Self {
111 Self::new(DefaultReferenceMap::new())
112 }
113}
114
115impl RootExt for ReferenceMap {}
116
117pub trait CustomReferenceMap : Debug + Downcast + Send + Sync {
118 /// Insert new element to the reference map. You may return false if it's not a valid label to stop parsing.
119 fn insert(&mut self, label: String, destination: String, title: Option<String>) -> bool;
120
121 /// Get an element referenced by `label` from the map, returns destination and optional title.
122 fn get(&self, label: &str) -> Option<(&str, Option<&str>)>;
123}
124
125impl_downcast!(CustomReferenceMap);
126
127#[derive(Default, Debug)]
128pub struct DefaultReferenceMap(HashMap<ReferenceMapKey, ReferenceMapEntry>);
129
130impl DefaultReferenceMap {
131 pub fn new() -> Self {
132 Self::default()
133 }
134
135 pub fn iter(&self) -> impl Iterator<Item = (&str, &str, Option<&str>)> {
136 Box::new(self.0.iter().map(|(a, b)| {
137 (a.label.as_str(), b.destination.as_str(), b.title.as_deref())
138 }))
139 }
140}
141
142impl CustomReferenceMap for DefaultReferenceMap {
143 fn insert(&mut self, label: String, destination: String, title: Option<String>) -> bool {
144 let Some(key) = ReferenceMapKey::new(label) else { return false; };
145 self.0.entry(key)
146 .or_insert(ReferenceMapEntry::new(destination, title));
147 true
148 }
149
150 fn get(&self, label: &str) -> Option<(&str, Option<&str>)> {
151 let key = ReferenceMapKey::new(label.to_owned())?;
152 self.0.get(&key)
153 .map(|r| (r.destination.as_str(), r.title.as_deref()))
154 }
155}
156
157#[derive(Derivative)]
158#[derivative(Debug, Default, Hash, PartialEq, Eq)]
159/// Reference label
160struct ReferenceMapKey {
161 #[derivative(PartialEq = "ignore")]
162 #[derivative(Hash = "ignore")]
163 pub label: String,
164 normalized: String,
165}
166
167impl ReferenceMapKey {
168 pub fn new(label: String) -> Option<Self> {
169 let normalized = normalize_reference(&label);
170
171 if normalized.is_empty() {
172 // CommonMark 0.20 disallows empty labels
173 return None;
174 }
175
176 Some(Self { label, normalized })
177 }
178}
179
180#[derive(Debug, Default)]
181/// Reference value
182struct ReferenceMapEntry {
183 pub destination: String,
184 pub title: Option<String>,
185}
186
187impl ReferenceMapEntry {
188 pub fn new(destination: String, title: Option<String>) -> Self {
189 Self { destination, title }
190 }
191}
192
193/// Add plugin that parses markdown link references
194pub fn add(md: &mut MarkdownIt) {
195 md.block.add_rule::<ReferenceScanner>();
196}
197
198#[derive(Debug)]
199pub struct Definition {
200 pub label: String,
201 pub destination: String,
202 pub title: Option<String>,
203}
204impl NodeValue for Definition {
205 fn render(&self, _: &Node, _: &mut dyn crate::Renderer) {}
206}
207
208#[doc(hidden)]
209pub struct ReferenceScanner;
210impl BlockRule for ReferenceScanner {
211 fn check(_: &mut BlockState) -> Option<()> {
212 None // can't interrupt anything
213 }
214
215 fn run(state: &mut BlockState) -> Option<(Node, usize)> {
216
217 if state.line_indent(state.line) >= state.md.max_indent { return None; }
218
219 let mut chars = state.get_line(state.line).chars();
220
221 let Some('[') = chars.next() else { return None; };
222
223 // Simple check to quickly interrupt scan on [link](url) at the start of line.
224 // Can be useful on practice: https://github.com/markdown-it/markdown-it/issues/54
225 loop {
226 match chars.next() {
227 Some('\\') => { chars.next(); },
228 Some(']') => {
229 if let Some(':') = chars.next() {
230 break;
231 } else {
232 return None;
233 }
234 }
235 Some(_) => {},
236 None => break,
237 }
238 }
239
240 let start_line = state.line;
241 let mut next_line = start_line;
242
243 // jump line-by-line until empty one or EOF
244 'outer: loop {
245 next_line += 1;
246
247 if next_line >= state.line_max || state.is_empty(next_line) { break; }
248
249 // this may be a code block normally, but after paragraph
250 // it's considered a lazy continuation regardless of what's there
251 if state.line_indent(next_line) >= state.md.max_indent { continue; }
252
253 // quirk for blockquotes, this line should already be checked by that rule
254 if state.line_offsets[next_line].indent_nonspace < 0 { continue; }
255
256 // Some tags can terminate paragraph without empty line.
257 let old_state_line = state.line;
258 state.line = next_line;
259 if state.test_rules_at_line() {
260 state.line = old_state_line;
261 break 'outer;
262 }
263 state.line = old_state_line;
264 }
265
266 let (str_before_trim, _) = state.get_lines(start_line, next_line, state.blk_indent, false);
267 let str = str_before_trim.trim();
268 let mut chars = str.char_indices();
269 chars.next(); // skip '['
270 let label_end;
271 let mut lines = 0;
272
273 loop {
274 match chars.next() {
275 Some((_, '[')) => return None,
276 Some((p, ']')) => {
277 label_end = p;
278 break;
279 }
280 Some((_, '\n')) => lines += 1,
281 Some((_, '\\')) => {
282 if let Some((_, '\n')) = chars.next() {
283 lines += 1;
284 }
285 }
286 Some(_) => {},
287 None => return None,
288 }
289 }
290
291 let Some((_, ':')) = chars.next() else { return None; };
292
293 // [label]: destination 'title'
294 // ^^^ skip optional whitespace here
295 let mut pos = label_end + 2;
296 while let Some((_, ch @ (' ' | '\t' | '\n'))) = chars.next() {
297 if ch == '\n' { lines += 1; }
298 pos += 1;
299 }
300
301 // [label]: destination 'title'
302 // ^^^^^^^^^^^ parse this
303 let href;
304 if let Some(res) = full_link::parse_link_destination(str, pos, str.len()) {
305 if pos == res.pos { return None; }
306 href = state.md.link_formatter.normalize_link(&res.str);
307 state.md.link_formatter.validate_link(&href)?;
308 pos = res.pos;
309 lines += res.lines;
310 } else {
311 return None;
312 }
313
314 // save cursor state, we could require to rollback later
315 let dest_end_pos = pos;
316 let dest_end_lines = lines;
317
318 // [label]: destination 'title'
319 // ^^^ skipping those spaces
320 let start = pos;
321 let mut chars = str[pos..].chars();
322 while let Some(ch @ (' ' | '\t' | '\n')) = chars.next() {
323 if ch == '\n' { lines += 1; }
324 pos += 1;
325 }
326
327 // [label]: destination 'title'
328 // ^^^^^^^ parse this
329 let mut title = None;
330 if pos != start {
331 if let Some(res) = full_link::parse_link_title(str, pos, str.len()) {
332 title = Some(res.str);
333 pos = res.pos;
334 lines += res.lines;
335 } else {
336 pos = dest_end_pos;
337 lines = dest_end_lines;
338 }
339 }
340
341 // skip trailing spaces until the rest of the line
342 let mut chars = str[pos..].chars();
343 loop {
344 match chars.next() {
345 Some(' ' | '\t') => pos += 1,
346 Some('\n') | None => break,
347 Some(_) if title.is_some() => {
348 // garbage at the end of the line after title,
349 // but it could still be a valid reference if we roll back
350 title = None;
351 pos = dest_end_pos;
352 lines = dest_end_lines;
353 chars = str[pos..].chars();
354 }
355 Some(_) => {
356 // garbage at the end of the line
357 return None;
358 }
359 }
360 }
361
362 let references = state.root_ext.get_or_insert_default::<ReferenceMap>();
363 if !references.insert(str[1..label_end].to_owned(), href.clone(), title.clone()) { return None; }
364
365 Some((Node::new(
366 Definition {
367 label: str[1..label_end].to_owned(),
368 destination: href,
369 title
370 }),
371 lines + 1
372 ))
373 }
374}
crates/markdown-it/src/plugins/cmark/inline/autolink.rs created+87
...@@ -0,0 +1,87 @@
1//! Autolinks
2//!
3//! `<https://example.org>`
4//!
5//! <https://spec.commonmark.org/0.30/#autolinks>
6use once_cell::sync::Lazy;
7use regex::Regex;
8
9use crate::parser::inline::{InlineRule, InlineState, TextSpecial};
10use crate::{MarkdownIt, Node, NodeValue, Renderer};
11
12#[derive(Debug)]
13pub struct Autolink {
14 pub url: String,
15}
16
17impl NodeValue for Autolink {
18 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
19 let mut attrs = node.attrs.clone();
20 attrs.push(("href", self.url.clone()));
21
22 fmt.open("a", &attrs);
23 fmt.contents(&node.children);
24 fmt.close("a");
25 }
26}
27
28pub fn add(md: &mut MarkdownIt) {
29 md.inline.add_rule::<AutolinkScanner>();
30}
31
32static AUTOLINK_RE : Lazy<Regex> = Lazy::new(|| {
33 Regex::new(r"^([a-zA-Z][a-zA-Z0-9+.\-]{1,31}):([^<>\x00-\x20]*)$").unwrap()
34});
35
36static EMAIL_RE : Lazy<Regex> = Lazy::new(|| {
37 Regex::new(r"^([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)$").unwrap()
38});
39
40#[doc(hidden)]
41pub struct AutolinkScanner;
42impl InlineRule for AutolinkScanner {
43 const MARKER: char = '<';
44
45 fn run(state: &mut InlineState) -> Option<(Node, usize)> {
46 let mut chars = state.src[state.pos..state.pos_max].chars();
47 if chars.next().unwrap() != '<' { return None; }
48
49 let mut pos = state.pos + 2;
50
51 loop {
52 match chars.next() {
53 Some('<') | None => return None,
54 Some('>') => break,
55 Some(x) => pos += x.len_utf8(),
56 }
57 }
58
59 let url = &state.src[state.pos+1..pos-1];
60 let is_autolink = AUTOLINK_RE.is_match(url);
61 let is_email = EMAIL_RE.is_match(url);
62
63 if !is_autolink && !is_email { return None; }
64
65 let full_url = if is_autolink {
66 state.md.link_formatter.normalize_link(url)
67 } else {
68 state.md.link_formatter.normalize_link(&("mailto:".to_owned() + url))
69 };
70
71 state.md.link_formatter.validate_link(&full_url)?;
72
73 let content = state.md.link_formatter.normalize_link_text(url);
74
75 let mut inner_node = Node::new(TextSpecial {
76 content: content.clone(),
77 markup: content,
78 info: "autolink",
79 });
80 inner_node.srcmap = state.get_map(state.pos + 1, pos - 1);
81
82 let mut node = Node::new(Autolink { url: full_url });
83 node.children.push(inner_node);
84
85 Some((node, pos - state.pos))
86 }
87}
crates/markdown-it/src/plugins/cmark/inline/backticks.rs created+28
...@@ -0,0 +1,28 @@
1//! Code spans
2//!
3//! `` `looks like this` ``
4//!
5//! <https://spec.commonmark.org/0.30/#code-span>
6use crate::generics::inline::code_pair;
7use crate::{MarkdownIt, Node, NodeValue, Renderer};
8
9#[derive(Debug)]
10pub struct CodeInline {
11 pub marker: char,
12 pub marker_len: usize,
13}
14
15impl NodeValue for CodeInline {
16 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
17 fmt.open("code", &node.attrs);
18 fmt.contents(&node.children);
19 fmt.close("code");
20 }
21}
22
23pub fn add(md: &mut MarkdownIt) {
24 code_pair::add_with::<'`'>(md, |len| Node::new(CodeInline {
25 marker: '`',
26 marker_len: len,
27 }));
28}
crates/markdown-it/src/plugins/cmark/inline/emphasis.rs created+40
...@@ -0,0 +1,40 @@
1//! Emphasis and strong emphasis
2//!
3//! looks like `*this*` or `__that__`
4//!
5//! <https://spec.commonmark.org/0.30/#emphasis-and-strong-emphasis>
6use crate::generics::inline::emph_pair;
7use crate::{MarkdownIt, Node, NodeValue, Renderer};
8
9#[derive(Debug)]
10pub struct Em {
11 pub marker: char
12}
13
14impl NodeValue for Em {
15 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
16 fmt.open("em", &node.attrs);
17 fmt.contents(&node.children);
18 fmt.close("em");
19 }
20}
21
22#[derive(Debug)]
23pub struct Strong {
24 pub marker: char
25}
26
27impl NodeValue for Strong {
28 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
29 fmt.open("strong", &node.attrs);
30 fmt.contents(&node.children);
31 fmt.close("strong");
32 }
33}
34
35pub fn add(md: &mut MarkdownIt) {
36 emph_pair::add_with::<'*', 1, true> (md, || Node::new(Em { marker: '*' }));
37 emph_pair::add_with::<'_', 1, false> (md, || Node::new(Em { marker: '_' }));
38 emph_pair::add_with::<'*', 2, true> (md, || Node::new(Strong { marker: '*' }));
39 emph_pair::add_with::<'_', 2, false> (md, || Node::new(Strong { marker: '_' }));
40}
crates/markdown-it/src/plugins/cmark/inline/entity.rs created+85
...@@ -0,0 +1,85 @@
1//! Entity and numeric character references
2//!
3//! `&#123;`, `&#xAF;`, `&quot;`
4//!
5//! <https://spec.commonmark.org/0.30/#entity-and-numeric-character-references>
6use once_cell::sync::Lazy;
7use regex::Regex;
8
9use crate::common::utils::{get_entity_from_str, is_valid_entity_code};
10use crate::parser::inline::{InlineRule, InlineState, TextSpecial};
11use crate::{MarkdownIt, Node};
12
13pub fn add(md: &mut MarkdownIt) {
14 md.inline.add_rule::<EntityScanner>();
15}
16
17static DIGITAL_RE : Lazy<Regex> = Lazy::new(|| {
18 Regex::new("(?i)^&#((?:x[a-f0-9]{1,6}|[0-9]{1,7}));").unwrap()
19});
20
21static NAMED_RE : Lazy<Regex> = Lazy::new(|| {
22 Regex::new("(?i)^&([a-z][a-z0-9]{1,31});").unwrap()
23});
24
25#[doc(hidden)]
26pub struct EntityScanner;
27
28impl EntityScanner {
29 fn parse_digital_entity(state: &mut InlineState) -> Option<(Node, usize)> {
30 let capture = DIGITAL_RE.captures(&state.src[state.pos..])?;
31 let entity_len = capture[0].len();
32 let entity = &capture[1];
33 #[allow(clippy::from_str_radix_10)]
34 let code = if entity.starts_with('x') || entity.starts_with('X') {
35 u32::from_str_radix(&entity[1..], 16).unwrap()
36 } else {
37 u32::from_str_radix(entity, 10).unwrap()
38 };
39
40 let content_str = if is_valid_entity_code(code) {
41 char::from_u32(code).unwrap().into()
42 } else {
43 '\u{FFFD}'.into()
44 };
45
46 let markup_str = capture[0].to_owned();
47
48 let node = Node::new(TextSpecial {
49 content: content_str,
50 markup: markup_str,
51 info: "entity",
52 });
53 Some((node, entity_len))
54 }
55
56 fn parse_named_entity(state: &mut InlineState) -> Option<(Node, usize)> {
57 let capture = NAMED_RE.captures(&state.src[state.pos..])?;
58 let str = get_entity_from_str(&capture[0])?;
59 let entity_len = capture[0].len();
60 let markup_str = capture[0].to_owned();
61 let content_str = (*str).to_owned();
62
63 let node = Node::new(TextSpecial {
64 content: content_str,
65 markup: markup_str,
66 info: "entity",
67 });
68 Some((node, entity_len))
69 }
70}
71
72impl InlineRule for EntityScanner {
73 const MARKER: char = '&';
74
75 fn run(state: &mut InlineState) -> Option<(Node, usize)> {
76 let mut chars = state.src[state.pos..state.pos_max].chars();
77 if chars.next().unwrap() != '&' { return None; }
78
79 if let Some('#') = chars.next() {
80 Self::parse_digital_entity(state)
81 } else {
82 Self::parse_named_entity(state)
83 }
84 }
85}
crates/markdown-it/src/plugins/cmark/inline/escape.rs created+57
...@@ -0,0 +1,57 @@
1//! Backslash escapes
2//!
3//! Allows escapes like `\*hello*`, also processes hard breaks at the end
4//! of the line.
5//!
6//! <https://spec.commonmark.org/0.30/#backslash-escapes>
7use crate::parser::inline::{InlineRule, InlineState, TextSpecial};
8use crate::plugins::cmark::inline::newline::Hardbreak;
9use crate::{MarkdownIt, Node};
10
11pub fn add(md: &mut MarkdownIt) {
12 md.inline.add_rule::<EscapeScanner>();
13}
14
15#[doc(hidden)]
16pub struct EscapeScanner;
17impl InlineRule for EscapeScanner {
18 const MARKER: char = '\\';
19
20 fn run(state: &mut InlineState) -> Option<(Node, usize)> {
21 let mut chars = state.src[state.pos..state.pos_max].chars();
22 if chars.next().unwrap() != '\\' { return None; }
23
24 match chars.next() {
25 Some('\n') => {
26 // skip leading whitespaces from next line
27 let mut len = 2;
28 while let Some(' ' | '\t') = chars.next() {
29 len += 1;
30 }
31 Some((Node::new(Hardbreak), len))
32 }
33 Some(chr) => {
34 let start = state.pos;
35 let end = state.pos + 1 + chr.len_utf8();
36
37 let mut orig_str = "\\".to_owned();
38 orig_str.push(chr);
39
40 let content_str = match chr {
41 '\\' | '!' | '"' | '#' | '$' | '%' | '&' | '\'' | '(' | ')' |
42 '*' | '+' | ',' | '.' | '/' | ':' | ';' | '<' | '=' | '>' | '?' |
43 '@' | '[' | ']' | '^' | '_' | '`' | '{' | '|' | '}' | '~' | '-' => chr.into(),
44 _ => orig_str.clone()
45 };
46
47 let node = Node::new(TextSpecial {
48 content: content_str,
49 markup: orig_str,
50 info: "escape",
51 });
52 Some((node, end - start))
53 }
54 None => None
55 }
56 }
57}
crates/markdown-it/src/plugins/cmark/inline/image.rs created+34
...@@ -0,0 +1,34 @@
1//! Images
2//!
3//! `![image](<src> "title")`
4//!
5//! <https://spec.commonmark.org/0.30/#images>
6use crate::generics::inline::full_link;
7use crate::{MarkdownIt, Node, NodeValue, Renderer};
8
9#[derive(Debug)]
10pub struct Image {
11 pub url: String,
12 pub title: Option<String>,
13}
14
15impl NodeValue for Image {
16 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
17 let mut attrs = node.attrs.clone();
18 attrs.push(("src", self.url.clone()));
19 attrs.push(("alt", node.collect_text()));
20
21 if let Some(title) = &self.title {
22 attrs.push(("title", title.clone()));
23 }
24
25 fmt.self_close("img", &attrs);
26 }
27}
28
29pub fn add(md: &mut MarkdownIt) {
30 full_link::add_prefix::<'!', true>(md, |href, title| Node::new(Image {
31 url: href.unwrap_or_default(),
32 title,
33 }));
34}
crates/markdown-it/src/plugins/cmark/inline/link.rs created+35
...@@ -0,0 +1,35 @@
1//! Links
2//!
3//! `![link](<to> "stuff")`
4//!
5//! <https://spec.commonmark.org/0.30/#links>
6use crate::generics::inline::full_link;
7use crate::{MarkdownIt, Node, NodeValue, Renderer};
8
9#[derive(Debug)]
10pub struct Link {
11 pub url: String,
12 pub title: Option<String>,
13}
14
15impl NodeValue for Link {
16 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
17 let mut attrs = node.attrs.clone();
18 attrs.push(("href", self.url.clone()));
19
20 if let Some(title) = &self.title {
21 attrs.push(("title", title.clone()));
22 }
23
24 fmt.open("a", &attrs);
25 fmt.contents(&node.children);
26 fmt.close("a");
27 }
28}
29
30pub fn add(md: &mut MarkdownIt) {
31 full_link::add::<false>(md, |href, title| Node::new(Link {
32 url: href.unwrap_or_default(),
33 title,
34 }));
35}
crates/markdown-it/src/plugins/cmark/inline/mod.rs created+9
...@@ -0,0 +1,9 @@
1//! Inline-level CommonMark structures.
2pub mod autolink;
3pub mod backticks;
4pub mod emphasis;
5pub mod entity;
6pub mod escape;
7pub mod image;
8pub mod link;
9pub mod newline;
crates/markdown-it/src/plugins/cmark/inline/newline.rs created+81
...@@ -0,0 +1,81 @@
1//! Line breaks
2//!
3//! Processes EOL (`\n`, soft and hard breaks).
4//!
5//! - <https://spec.commonmark.org/0.30/#hard-line-breaks>
6//! - <https://spec.commonmark.org/0.30/#soft-line-breaks>
7use crate::parser::inline::{InlineRule, InlineState};
8use crate::{MarkdownIt, Node, NodeValue, Renderer};
9
10#[derive(Debug)]
11pub struct Hardbreak;
12
13impl NodeValue for Hardbreak {
14 fn render(&self, _: &Node, fmt: &mut dyn Renderer) {
15 fmt.self_close("br", &[]);
16 fmt.cr();
17 }
18}
19
20#[derive(Debug)]
21pub struct Softbreak;
22
23impl NodeValue for Softbreak {
24 fn render(&self, _: &Node, fmt: &mut dyn Renderer) {
25 fmt.cr();
26 }
27}
28
29pub fn add(md: &mut MarkdownIt) {
30 md.inline.add_rule::<NewlineScanner>();
31}
32
33#[doc(hidden)]
34pub struct NewlineScanner;
35impl InlineRule for NewlineScanner {
36 const MARKER: char = '\n';
37
38 fn check(state: &mut InlineState) -> Option<usize> {
39 // check rule is required because run() modifies trailing text
40 let mut chars = state.src[state.pos..state.pos_max].chars();
41 if chars.next().unwrap() != '\n' { return None; }
42 Some(1)
43 }
44
45 fn run(state: &mut InlineState) -> Option<(Node, usize)> {
46 let mut chars = state.src[state.pos..state.pos_max].chars();
47
48 if chars.next().unwrap() != '\n' { return None; }
49
50 let mut pos = state.pos;
51 pos += 1;
52
53 // skip leading whitespaces from next line
54 while let Some(' ' | '\t') = chars.next() {
55 pos += 1;
56 }
57
58 // ' \n' -> hardbreak
59 let mut tail_size = 0;
60 let trailing_text = state.trailing_text_get();
61
62 for ch in trailing_text.chars().rev() {
63 if ch == ' ' {
64 tail_size += 1;
65 } else {
66 break;
67 }
68 }
69
70 state.trailing_text_pop(tail_size);
71
72 let node = if tail_size >= 2 {
73 Node::new(Hardbreak)
74 } else {
75 Node::new(Softbreak)
76 };
77
78 state.pos -= tail_size; // backtrack to include tail in source maps
79 Some((node, pos - state.pos))
80 }
81}
crates/markdown-it/src/plugins/cmark/mod.rs created+34
...@@ -0,0 +1,34 @@
1//! Basic markdown syntax, you probably want to add this.
2//!
3//! This is full implementation of [CommonMark](https://spec.commonmark.org/0.30/)
4//! standard (with the exception of HTML inlines/blocks which are moved to separate
5//! [plugin](crate::plugins::html) for security reasons).
6//!
7//! [cmark::add](self::add) function adds all features at once. If you only want
8//! to enable some of it (e.g. disable images), you can add each syntax one by one
9//! by invoking `add` function of the respective module.
10pub mod block;
11pub mod inline;
12
13use crate::MarkdownIt;
14
15pub fn add(md: &mut MarkdownIt) {
16 inline::newline::add(md);
17 inline::escape::add(md);
18 inline::backticks::add(md);
19 inline::emphasis::add(md);
20 inline::link::add(md);
21 inline::image::add(md);
22 inline::autolink::add(md);
23 inline::entity::add(md);
24
25 block::code::add(md);
26 block::fence::add(md);
27 block::blockquote::add(md);
28 block::hr::add(md);
29 block::list::add(md);
30 block::reference::add(md);
31 block::heading::add(md);
32 block::lheading::add(md);
33 block::paragraph::add(md);
34}
crates/markdown-it/src/plugins/extra/beautify_links.rs created+39
...@@ -0,0 +1,39 @@
1//! Pretty-print all urls and fit them into N characters
2
3use crate::parser::linkfmt::{LinkFormatter, MDLinkFormatter};
4use crate::MarkdownIt;
5
6#[derive(Debug)]
7struct LinkBeautifier {
8 max_length: usize,
9 parent: Box<dyn LinkFormatter>,
10}
11
12impl LinkFormatter for LinkBeautifier {
13 fn validate_link(&self, url: &str) -> Option<()> {
14 self.parent.as_ref().validate_link(url)
15 }
16
17 fn normalize_link(&self, url: &str) -> String {
18 mdurl::format_url_for_computers(url)
19 }
20
21 fn normalize_link_text(&self, url: &str) -> String {
22 mdurl::format_url_for_humans(url, self.max_length)
23 }
24}
25
26
27/// Add beautifier plugin, limiting urls to default 50 characters
28pub fn add(md: &mut MarkdownIt) {
29 add_with_char_limit(md, 50);
30}
31
32/// Add beautifier plugin, limiting urls to `max_length` characters
33pub fn add_with_char_limit(md: &mut MarkdownIt, max_length: usize) {
34 let parent = std::mem::replace(&mut md.link_formatter, Box::new(MDLinkFormatter::new()));
35 md.link_formatter = Box::new(LinkBeautifier {
36 max_length,
37 parent,
38 });
39}
crates/markdown-it/src/plugins/extra/heading_anchors.rs created+69
...@@ -0,0 +1,69 @@
1//! Add id attribute (slug) to headings.
2//!
3//! ```rust
4//! // it is recommended to use 3rd party slug implementation
5//! //let slugify_fn = |s: &str| slug::slugify(s);
6//! let slugify_fn = markdown_it::plugins::extra::heading_anchors::simple_slugify_fn;
7//!
8//! let md = &mut markdown_it::MarkdownIt::new();
9//! markdown_it::plugins::cmark::add(md);
10//! markdown_it::plugins::extra::heading_anchors::add(md, slugify_fn);
11//!
12//! assert_eq!(
13//! md.parse("## An example heading").render(),
14//! "<h2 id=\"an-example-heading\">An example heading</h2>\n",
15//! );
16//! ```
17use std::fmt::Debug;
18
19use crate::parser::core::CoreRule;
20use crate::parser::extset::MarkdownItExt;
21use crate::plugins::cmark::block::heading::ATXHeading;
22use crate::plugins::cmark::block::lheading::SetextHeader;
23use crate::{MarkdownIt, Node};
24
25pub fn add(md: &mut MarkdownIt, slugify: fn (&str) -> String) {
26 md.ext.insert(SlugifyFunction(slugify));
27 md.add_rule::<AddHeadingAnchors>();
28}
29
30/// Simple built-in slugify function. It is added for testing and demonstration
31/// purposes only, you should be using `slug`/`slugify` crate instead or your own impl.
32pub fn simple_slugify_fn(s: &str) -> String {
33 s.chars().map(|x| {
34 if x.is_alphanumeric() {
35 x.to_ascii_lowercase()
36 } else {
37 '-'
38 }
39 }).collect()
40}
41
42#[derive(Clone, Copy)]
43struct SlugifyFunction(fn (&str) -> String);
44impl MarkdownItExt for SlugifyFunction {}
45
46impl Default for SlugifyFunction {
47 fn default() -> Self {
48 Self(simple_slugify_fn)
49 }
50}
51
52impl Debug for SlugifyFunction {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 f.debug_struct("SlugifyFunction").finish()
55 }
56}
57
58pub struct AddHeadingAnchors;
59impl CoreRule for AddHeadingAnchors {
60 fn run(root: &mut Node, md: &MarkdownIt) {
61 let slugify = md.ext.get::<SlugifyFunction>().copied().unwrap_or_default().0;
62
63 root.walk_mut(|node, _| {
64 if node.is::<ATXHeading>() || node.is::<SetextHeader>() {
65 node.attrs.push(("id", slugify(&node.collect_text())));
66 }
67 });
68 }
69}
crates/markdown-it/src/plugins/extra/linkify.rs created+135
...@@ -0,0 +1,135 @@
1//! Find urls and emails, and turn them into links
2
3use linkify::{LinkFinder, LinkKind};
4use once_cell::sync::Lazy;
5use regex::Regex;
6use std::cmp::Ordering;
7
8use crate::parser::core::{CoreRule, Root};
9use crate::parser::extset::RootExt;
10use crate::parser::inline::builtin::InlineParserRule;
11use crate::parser::inline::{InlineRule, InlineState, TextSpecial};
12use crate::{MarkdownIt, Node, NodeValue, Renderer};
13
14static SCHEME_RE : Lazy<Regex> = Lazy::new(|| {
15 Regex::new(r"(?i)(?:^|[^a-z0-9.+-])([a-z][a-z0-9.+-]*)$").unwrap()
16});
17
18#[derive(Debug)]
19pub struct Linkified {
20 pub url: String,
21}
22
23impl NodeValue for Linkified {
24 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
25 let mut attrs = node.attrs.clone();
26 attrs.push(("href", self.url.clone()));
27
28 fmt.open("a", &attrs);
29 fmt.contents(&node.children);
30 fmt.close("a");
31 }
32}
33
34pub fn add(md: &mut MarkdownIt) {
35 md.add_rule::<LinkifyPrescan>()
36 .before::<InlineParserRule>();
37
38 md.inline.add_rule::<LinkifyScanner>();
39}
40
41type LinkifyState = Vec<LinkifyPosition>;
42impl RootExt for LinkifyState {}
43
44#[derive(Debug, Clone, Copy)]
45struct LinkifyPosition {
46 start: usize,
47 end: usize,
48 //email: bool,
49}
50
51#[doc(hidden)]
52pub struct LinkifyPrescan;
53impl CoreRule for LinkifyPrescan {
54 fn run(root: &mut Node, _: &MarkdownIt) {
55 let root_data = root.cast_mut::<Root>().unwrap();
56 let source = root_data.content.as_str();
57 let finder = LinkFinder::new();
58 let positions = finder.links(source).filter_map(|link| {
59 if *link.kind() == LinkKind::Url {
60 Some(LinkifyPosition {
61 start: link.start(),
62 end: link.end(),
63 //email: *link.kind() == LinkKind::Email,
64 })
65 } else {
66 None
67 }
68 }).collect::<Vec<_>>();
69 root_data.ext.insert(positions);
70 }
71}
72
73#[doc(hidden)]
74pub struct LinkifyScanner;
75impl InlineRule for LinkifyScanner {
76 const MARKER: char = ':';
77
78 fn run(state: &mut InlineState) -> Option<(Node, usize)> {
79 let mut chars = state.src[state.pos..state.pos_max].chars();
80 if chars.next().unwrap() != ':' { return None; }
81 if state.link_level > 0 { return None; }
82
83 let trailing = state.trailing_text_get();
84 if !SCHEME_RE.is_match(trailing) { return None; }
85
86 let map = state.get_map(state.pos, state.pos_max)?;
87 let (start, _) = map.get_byte_offsets();
88
89 let positions = state.root_ext.get::<LinkifyState>().unwrap();
90
91 let found_idx = positions.binary_search_by(|x| {
92 if x.start >= start {
93 Ordering::Greater
94 } else if x.end <= start {
95 Ordering::Less
96 } else {
97 Ordering::Equal
98 }
99 }).ok()?;
100
101 let found = positions[found_idx];
102 let proto_size = start - found.start;
103 if proto_size > trailing.len() { return None; }
104
105 debug_assert_eq!(
106 &trailing[trailing.len()-proto_size..],
107 &state.src[state.pos-proto_size..state.pos]
108 );
109
110 let url_start = state.pos - proto_size;
111 let url_end = state.pos - proto_size + found.end - found.start;
112 if url_end > state.pos_max { return None; }
113
114 let url = &state.src[url_start..url_end];
115 let full_url = state.md.link_formatter.normalize_link(url);
116
117 state.md.link_formatter.validate_link(&full_url)?;
118
119 let content = state.md.link_formatter.normalize_link_text(url);
120
121 let mut inner_node = Node::new(TextSpecial {
122 content: content.clone(),
123 markup: content,
124 info: "autolink",
125 });
126 inner_node.srcmap = state.get_map(url_start, url_end);
127
128 let mut node = Node::new(Linkified { url: full_url });
129 node.children.push(inner_node);
130
131 state.trailing_text_pop(proto_size);
132 state.pos -= proto_size;
133 Some((node, url_end - url_start))
134 }
135}
crates/markdown-it/src/plugins/extra/mod.rs created+44
...@@ -0,0 +1,44 @@
1//! Frequently used markdown extensions and stuff from GFM.
2//!
3//! - strikethrough (~~xxx~~~)
4//! - tables
5//! - linkify (convert http://example.com to a link)
6//! - beautify links (cut "http://" from links and shorten paths)
7//! - smartquotes and typographer
8//! - code block highlighting using `syntect`
9//!
10//! ```rust
11//! let md = &mut markdown_it::MarkdownIt::new();
12//! markdown_it::plugins::cmark::add(md);
13//! markdown_it::plugins::extra::add(md);
14//!
15//! let html = md.parse("hello ~~world~~").render();
16//! assert_eq!(html.trim(), r#"<p>hello <s>world</s></p>"#);
17//!
18//! let html = md.parse(r#"Markdown done "The Right Way(TM)""#).render();
19//! assert_eq!(html.trim(), r#"<p>Markdown done “The Right Way™”</p>"#);
20//! ```
21pub mod beautify_links;
22pub mod heading_anchors;
23#[cfg(feature = "linkify")]
24pub mod linkify;
25pub mod smartquotes;
26pub mod strikethrough;
27#[cfg(feature = "syntect")]
28pub mod syntect;
29pub mod tables;
30pub mod typographer;
31
32use crate::MarkdownIt;
33
34pub fn add(md: &mut MarkdownIt) {
35 strikethrough::add(md);
36 beautify_links::add(md);
37 #[cfg(feature = "linkify")]
38 linkify::add(md);
39 tables::add(md);
40 #[cfg(feature = "syntect")]
41 syntect::add(md);
42 typographer::add(md);
43 smartquotes::add(md);
44}
crates/markdown-it/src/plugins/extra/smartquotes.rs created+567
...@@ -0,0 +1,567 @@
1//! Replaces `"` and `'` quotes with "nicer" ones like `‘`, `’`, `“`, `”`, or
2//! with `’` for words like "isn't".
3//!
4//! This currently only supports single character quotes, which is a limitation
5//! of the Rust implementation due to the use of `const` generics.
6//!
7//! ## Implementation notes
8//!
9//! The main obstacle to implementing this was the fact that the document is
10//! necessarily represented as a tree of nodes.
11//! Each node is thus necessarily referenced by its parents, which means that an
12//! any given moment we cannot hold a mutable reference to a node if any other
13//! part of the code holds a reference to the document. At least that's my
14//! understanding of the problem.
15//! The smartquotes algorithm from the JS library makes heavy use of iteration
16//! backwards and forwards through a flat list of tokens. This isn't really
17//! possible in the Rust implementation. Building a flat representation of all
18//! `Node` objects is easy, but holding that list precludes us from executing a
19//! `root.walk_mut` call at the same time.
20//! On top of that, while the smartquotes algorithm iterates linearly over all
21//! nodes/tokens, looking at a specific token with index `j` can trigger
22//! replacements in any of the tokens with `0` to `j - 1`.
23//!
24//! The solution proposed here is to first compute all the replacement
25//! operations on a read-only flat view of the document, and _then_ to perform
26//! all replacements in a single call to `root.walk_mut`.
27use std::collections::HashMap;
28
29use crate::common::utils::is_punct_char;
30use crate::parser::core::CoreRule;
31use crate::parser::inline::Text;
32use crate::plugins::cmark::block::paragraph::Paragraph;
33use crate::plugins::cmark::inline::newline::{Hardbreak, Softbreak};
34use crate::plugins::html::html_inline::HtmlInline;
35use crate::{MarkdownIt, Node};
36
37const APOSTROPHE: char = '\u{2019}';
38const SINGLE_QUOTE: char = '\'';
39const DOUBLE_QUOTE: char = '"';
40const SPACE: char = ' ';
41
42/// Add smartquotes with the "classic" quote set of `‘`, `’`, `“`, and `”`.
43pub fn add(md: &mut MarkdownIt) {
44 add_with::<'‘', '’', '“', '”'>(md);
45}
46
47pub fn add_with<
48 const OPEN_SINGLE_QUOTE: char,
49 const CLOSE_SINGLE_QUOTE: char,
50 const OPEN_DOUBLE_QUOTE: char,
51 const CLOSE_DOUBLE_QUOTE: char,
52>(
53 md: &mut MarkdownIt,
54) {
55 md.add_rule::<SmartQuotesRule<
56 OPEN_SINGLE_QUOTE,
57 CLOSE_SINGLE_QUOTE,
58 OPEN_DOUBLE_QUOTE,
59 CLOSE_DOUBLE_QUOTE>>();
60}
61
62/// Simplified Node type that only holds the info we need
63///
64/// To replace quotes, we'll be iterating forward and backward over the nodes in
65/// our document tree. The `Node` class doesn't provide a mechanism to do this
66/// efficiently, and in any case we only care about certain parts of the
67/// information. This struct will be used to build a flat view of the document;
68/// the `Irrelevant` variant serves as a "filler" so that the indexes of the
69/// entries line up correctly with the order we see during tree traversal.
70enum FlatToken<'a> {
71 LineBreak,
72 Text {
73 content: &'a str,
74 nesting_level: u32,
75 },
76 HtmlInline {
77 content: &'a str,
78 },
79 Irrelevant,
80}
81
82/// A simple enum to distinguish single and double quotes
83#[derive(PartialEq, Eq, Debug, Clone, Copy)]
84enum QuoteType {
85 Single,
86 Double,
87}
88
89/// Holds information about quotes we have encountered thus far.
90///
91/// These quotes may or may not be used to close a pair further down the line.
92/// The different fields thus hold all the information we need to a) decide
93/// whether or not to match them up with another quote we encounter, and b) to
94/// perform the correct replacement, should be indeed use this quote to close a
95/// pair.
96struct QuoteMarker {
97 /// The iteration index of the node in which this quote was found.
98 ///
99 /// This is the index at which this quote's `Node` appears in a pre-order
100 /// depth-first walk of the document tree. Since we can only _modify_ nodes
101 /// during a walk, we rely on this index to tell us which nodes to modify.
102 walk_index: usize,
103 /// The position of the quote within node's `content`
104 quote_position: usize,
105 /// Whether this is a single or a double quote
106 quote_type: QuoteType,
107 /// Nesting level of the containing token
108 ///
109 /// This is the nesting of the containing `Node` within the document tree.
110 /// It is used to decide which quotes can be matched up.
111 level: u32,
112}
113
114/// Description of a single quote replacement to be executed
115///
116/// As described above, we have to compute the replacements in a first step that
117/// treats the entire document tree read-only. Only then can we perform the
118/// actual replacements. This `struct` holds the information we need to perform
119/// the replacement of a single quote character during a `walk_mut`.
120struct ReplacementOp {
121 walk_index: usize,
122 quote_position: usize,
123 quote: char,
124}
125
126pub struct SmartQuotesRule<
127 const OPEN_SINGLE_QUOTE: char,
128 const CLOSE_SINGLE_QUOTE: char,
129 const OPEN_DOUBLE_QUOTE: char,
130 const CLOSE_DOUBLE_QUOTE: char,
131>;
132
133impl<
134 const OPEN_SINGLE_QUOTE: char,
135 const CLOSE_SINGLE_QUOTE: char,
136 const OPEN_DOUBLE_QUOTE: char,
137 const CLOSE_DOUBLE_QUOTE: char,
138 > CoreRule
139 for SmartQuotesRule<
140 OPEN_SINGLE_QUOTE,
141 CLOSE_SINGLE_QUOTE,
142 OPEN_DOUBLE_QUOTE,
143 CLOSE_DOUBLE_QUOTE,
144 >
145{
146 fn run(root: &mut Node, _: &MarkdownIt) {
147 let text_tokens = all_text_tokens(root);
148
149 let replacement_ops = Self::compute_replacements(text_tokens);
150
151 // now that we know what we want to replace where, we go over the nodes a _third_ time to do all the actual replacements.
152 let mut current_index: usize = 0;
153
154 root.walk_mut(|node, _| {
155 if let Some(current_replacements) = replacement_ops.get(&current_index) {
156 let text_node = node.cast_mut::<Text>()
157 .expect("Expected to find a text node at this index because we constructed our replacements HashMap accordingly.");
158 text_node.content = execute_replacements(current_replacements, &text_node.content);
159 };
160 current_index += 1;
161 });
162 }
163}
164
165impl<
166 const OPEN_SINGLE_QUOTE: char,
167 const CLOSE_SINGLE_QUOTE: char,
168 const OPEN_DOUBLE_QUOTE: char,
169 const CLOSE_DOUBLE_QUOTE: char,
170 >
171 SmartQuotesRule<OPEN_SINGLE_QUOTE, CLOSE_SINGLE_QUOTE, OPEN_DOUBLE_QUOTE, CLOSE_DOUBLE_QUOTE>
172{
173 /// Walk the list of tokens to figure out what needs replacing where. to do
174 /// this, we need to search back and forth over the nodes to find matching
175 /// quotes across nodes. The borrow checker won't let us handle the entire
176 /// set of nodes as mutable at the same time however, so all we do here is
177 /// figure out what we _want_ to replace in which node.
178 fn compute_replacements(text_tokens: Vec<FlatToken>) -> HashMap<usize, HashMap<usize, char>> {
179 let mut quote_stack: Vec<QuoteMarker> = Vec::new();
180 let mut replacement_ops: HashMap<usize, HashMap<usize, char>> = HashMap::new();
181 for (walk_index, token) in text_tokens.iter().enumerate() {
182 if let FlatToken::Text {
183 content,
184 nesting_level,
185 } = token
186 {
187 for op in Self::replace_smartquotes(
188 content,
189 walk_index,
190 *nesting_level,
191 &text_tokens,
192 &mut quote_stack,
193 ) {
194 replacement_ops
195 .entry(op.walk_index)
196 .or_default()
197 .insert(op.quote_position, op.quote);
198 }
199 }
200 }
201 replacement_ops
202 }
203
204 /// Compute quote replacements found by looking at a single text block
205 fn replace_smartquotes(
206 content: &str,
207 walk_index: usize,
208 level: u32,
209 text_tokens: &[FlatToken],
210 quote_stack: &mut Vec<QuoteMarker>,
211 ) -> Vec<ReplacementOp> {
212 truncate_stack(quote_stack, level);
213
214 let mut result: Vec<_> = Vec::new();
215 for (quote_position, quote_type) in find_quotes(content) {
216 let last_char = find_last_char_before(text_tokens, walk_index, quote_position);
217 let next_char = find_first_char_after(text_tokens, walk_index, quote_position);
218
219 let (can_open, can_close): (bool, bool) =
220 can_open_or_close(&quote_type, last_char, next_char);
221
222 if !can_open && !can_close {
223 // if this is a single quote then we're in the middle of a word and
224 // assume it to be an apostrophe
225 if quote_type == QuoteType::Single {
226 result.push(ReplacementOp {
227 walk_index,
228 quote_position,
229 quote: APOSTROPHE,
230 });
231 }
232 // in any case, we're done with this quote and continue searching
233 // for more quotes in this text block
234 continue;
235 }
236
237 if can_close {
238 if let Some((opening_op, closing_op, new_stack_len)) =
239 Self::try_close(quote_stack, walk_index, level, quote_type, quote_position)
240 {
241 quote_stack.truncate(new_stack_len);
242 result.push(opening_op);
243 result.push(closing_op);
244 continue;
245 }
246 }
247
248 if can_open {
249 quote_stack.push(QuoteMarker {
250 walk_index,
251 quote_position,
252 quote_type,
253 level,
254 });
255 } else if can_close && quote_type == QuoteType::Single {
256 result.push(ReplacementOp {
257 walk_index,
258 quote_position,
259 quote: APOSTROPHE,
260 });
261 }
262 }
263 result
264 }
265
266 /// Try to find a matching opening quote to the given one.
267 ///
268 /// If a match is found, returns `Some` with two `ReplacementOp`s to be
269 /// added to the result, and with the resulting length of the `quote_stack`.
270 fn try_close(
271 quote_stack: &[QuoteMarker],
272 walk_index: usize,
273 level: u32,
274 quote_type: QuoteType,
275 quote_position: usize,
276 ) -> Option<(ReplacementOp, ReplacementOp, usize)> {
277 for (j, other_item) in quote_stack.iter().enumerate().rev() {
278 if other_item.level < level {
279 return None;
280 }
281 if other_item.quote_type == quote_type && other_item.level == level {
282 return Some((
283 ReplacementOp {
284 walk_index: other_item.walk_index,
285 quote_position: other_item.quote_position,
286 quote: if quote_type == QuoteType::Single {
287 OPEN_SINGLE_QUOTE
288 } else {
289 OPEN_DOUBLE_QUOTE
290 },
291 },
292 ReplacementOp {
293 walk_index,
294 quote_position,
295 quote: if quote_type == QuoteType::Single {
296 CLOSE_SINGLE_QUOTE
297 } else {
298 CLOSE_DOUBLE_QUOTE
299 },
300 },
301 j,
302 ));
303 }
304 }
305 None
306 }
307}
308
309/// Produces a simplified flat list of all tokens, with the necessary
310/// information to do smart quote replacement.
311///
312/// This handles inline html and inline code like JS version seems to do.
313/// This list is a work-around for the fact that we can't build a flat list of
314/// all nodes for iteration back and forth, and at the same time do a mutable
315/// walk on the document tree.
316///
317/// Returns a `Vec<FlatToken<'a>>` where `<'a>` is the same lifetime as `root`.
318/// This simply reflects the fact that the `content: &str` entries of the
319/// `FlatToken` structs reference the same memory as `root`'s children.
320/// Every entry in the `Vec` will produce an entry in the result, meaning that
321/// the index of a token in the resulting `Vec` will be the same as the index it
322/// would get during a `root.walk` call.
323fn all_text_tokens(root: &Node) -> Vec<FlatToken> {
324 let mut result = Vec::new();
325 let mut walk_index = 0;
326 root.walk(|node, nesting_level| {
327 if let Some(text_node) = node.cast::<Text>() {
328 result.push(FlatToken::Text {
329 content: &text_node.content,
330 nesting_level,
331 });
332 } else if let Some(html_node) = node.cast::<HtmlInline>() {
333 result.push(FlatToken::HtmlInline {
334 content: &html_node.content,
335 });
336 } else if node.is::<Paragraph>() || node.is::<Hardbreak>() || node.is::<Softbreak>() {
337 result.push(FlatToken::LineBreak);
338 } else {
339 result.push(FlatToken::Irrelevant);
340 }
341 walk_index += 1;
342 });
343 result
344}
345
346/// Checks whether we can open or close a pair of quotes, given the quote type
347/// and the type of characters before and after the quote
348fn can_open_or_close(quote_type: &QuoteType, last_char: char, next_char: char) -> (bool, bool) {
349 // special case: 1"" -> count first quote as an inch
350 // We handle this before doing anything else to simplify the conditions
351 // below.
352 let is_double = *quote_type == QuoteType::Double;
353 let next_is_double = next_char == DOUBLE_QUOTE;
354 let last_is_digit = last_char.is_ascii_digit();
355 if next_is_double && is_double && last_is_digit {
356 return (false, false);
357 }
358
359 // using `is_ascii_punctuation` here matches the JS version exactly, but
360 // that also means we might inherit that implementation's shortcomings
361 // by ignoring unicode punctuation. `is_punct_char` however should
362 // compensate for this.
363 let is_last_punctuation = last_char.is_ascii_punctuation() || is_punct_char(last_char);
364 let is_next_punctuation = next_char.is_ascii_punctuation() || is_punct_char(next_char);
365
366 // Yet again we rely on rust's built-in character handling. The definition
367 // of `is_whitespace` according to the unicode proplist.txt shows that the
368 // difference to the JS version.
369 // https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt
370 //
371 // Recognized as whitespace by Rust, but not by JS:
372 // 0x85, 0x28, 0x29
373 let is_last_whitespace = last_char.is_whitespace();
374 let is_next_whitespace = next_char.is_whitespace();
375
376 let can_open =
377 !is_next_whitespace && (!is_next_punctuation || is_last_whitespace || is_last_punctuation);
378 let can_close =
379 !is_last_whitespace && (!is_last_punctuation || is_next_whitespace || is_next_punctuation);
380
381 if can_open && can_close {
382 // Replace quotes in the middle of punctuation sequence, but not
383 // in the middle of the words, i.e.:
384 //
385 // 1. foo " bar " baz - not replaced
386 // 2. foo-"-bar-"-baz - replaced
387 // 3. foo"bar"baz - not replaced
388 return (is_last_punctuation, is_next_punctuation);
389 }
390
391 (can_open, can_close)
392}
393
394/// Executes a set of character replacements on a string
395fn execute_replacements(replacement_ops: &HashMap<usize, char>, content: &str) -> String {
396 content
397 .chars()
398 .enumerate()
399 .map(|(i, c)| *replacement_ops.get(&i).unwrap_or(&c))
400 .collect()
401}
402
403/// Truncates the stack of quotes following the JS implementation.
404///
405/// This _might_ be simplified by removing the `rev` call and using
406/// `Vec::take_while` instead, but I'm not 100% sure yet that the levels on the
407/// stack are really monotonously increasing, so I'm leaving it as is for now.
408fn truncate_stack(quote_stack: &mut Vec<QuoteMarker>, level: u32) {
409 let stack_len = quote_stack
410 .iter()
411 .rev()
412 .skip_while(|qm| qm.level > level)
413 .count();
414 quote_stack.truncate(stack_len);
415}
416
417/// Finds all single or double quotes in a string, together with their positions
418///
419/// This might be replaced with a regex search, but not sure that's really worth
420/// it, given that we only check for two fixed characters.
421fn find_quotes(content: &str) -> impl Iterator<Item = (usize, QuoteType)> + '_ {
422 content.chars().enumerate().filter_map(|(p, c)| {
423 if c == SINGLE_QUOTE || c == DOUBLE_QUOTE {
424 Some((
425 p,
426 if c == SINGLE_QUOTE {
427 QuoteType::Single
428 } else {
429 QuoteType::Double
430 },
431 ))
432 } else {
433 None
434 }
435 })
436}
437
438/// Finds the next relevant character after a given position
439///
440/// This is the mirror image of `find_last_char_before`.
441///
442/// The position given is that of a quote we found. It is identified by its
443/// token/node index and the position of the quote inside that token. The full
444/// sequence of the text tokens is searched forwards from that point and the
445/// first character is returned.
446///
447/// If a line break or the end of the document is encountered during search,
448/// space (0x20) is returned.
449///
450/// This function is a bit simpler than `find_last_char_before` because Vec
451/// conveniently returns None for out-of-range indexes at the top end, while not
452/// allowing to index with negative index.
453fn find_first_char_after(
454 text_tokens: &[FlatToken],
455 token_index: usize,
456 quote_position: usize,
457) -> char {
458 for (idx_t, text_token) in text_tokens.iter().enumerate().skip(token_index) {
459 let token = match text_token {
460 FlatToken::LineBreak => return SPACE,
461 FlatToken::Text {
462 content,
463 nesting_level: _,
464 } => content,
465 FlatToken::HtmlInline {
466 content,
467 } => content,
468 FlatToken::Irrelevant => continue,
469 };
470 let start_index = if idx_t == token_index {
471 quote_position + 1
472 } else {
473 0
474 };
475 if let Some(c) = token.chars().nth(start_index) {
476 return c;
477 }
478 }
479 // this will be hit if we start searching at the last position of the last
480 // text token
481 SPACE
482}
483
484/// Finds the last relevant character before a given position
485///
486/// The position given is that of a quote we found. It is identified by its
487/// token/node index and the position of the quote inside that token. The full
488/// sequence of the text tokens is searched backwards from that point and the
489/// first character is returned.
490///
491/// If a line break or the beginning of the document is encountered during
492/// search, space (0x20) is returned.
493fn find_last_char_before(
494 text_tokens: &[FlatToken],
495 token_index: usize,
496 quote_position: usize,
497) -> char {
498 for idx_t in (0..=token_index).rev() {
499 let token = match &text_tokens[idx_t] {
500 FlatToken::LineBreak => return SPACE,
501 FlatToken::Text {
502 content,
503 nesting_level: _,
504 } => content,
505 FlatToken::HtmlInline {
506 content,
507 } => content,
508 FlatToken::Irrelevant => continue,
509 };
510
511 // this is _not_ the first index we want to look at, but rather the
512 // index just _after_ that. The reason is simply that this is `usize`
513 // and we want to first check if it's possible to still subtract 1 from
514 // it without panicking.
515 let start_index: usize = if idx_t == token_index {
516 quote_position
517 } else {
518 token.chars().count()
519 };
520 // means we can't go any further left -> try the next token (i.e. the
521 // one preceding this one)
522 if start_index == 0 {
523 continue;
524 }
525 // unwrapping is safe here, we built our index to match the length of
526 // the string, or (in the case of the token containing the quote itself)
527 // it should be indexing a _prefix_ of the string.
528 return token.chars().nth(start_index - 1).unwrap();
529 }
530 // this will be hit if we find a quote in the first position of the first token
531 SPACE
532}
533
534
535#[cfg(test)]
536mod tests {
537 #[test]
538 fn smartquotes_basics() {
539 let md = &mut crate::MarkdownIt::new();
540 crate::plugins::cmark::add(md);
541 crate::plugins::extra::smartquotes::add(md);
542 let html = md.parse(r#"'hello' "world""#).render();
543 assert_eq!(html.trim(), r#"<p>‘hello’ “world”</p>"#);
544 }
545
546 #[test]
547 fn smartquotes_shouldnt_affect_html() {
548 let md = &mut crate::MarkdownIt::new();
549 crate::plugins::cmark::add(md);
550 crate::plugins::html::html_inline::add(md);
551 crate::plugins::extra::smartquotes::add(md);
552 let html = md.parse(r#"<a href="hello"></a>"#).render();
553 assert_eq!(html.trim(), r#"<p><a href="hello"></a></p>"#);
554 }
555
556 #[test]
557 fn smartquotes_should_work_with_typographer() {
558 // regression test for https://github.com/rlidwka/markdown-it.rs/issues/26
559 let md = &mut crate::MarkdownIt::new();
560 crate::plugins::cmark::add(md);
561 crate::plugins::html::html_inline::add(md);
562 crate::plugins::extra::typographer::add(md);
563 crate::plugins::extra::smartquotes::add(md);
564 let html = md.parse("\"**...**\"").render();
565 assert_eq!(html.trim(), "<p>“<strong>…</strong>”</p>");
566 }
567}
crates/markdown-it/src/plugins/extra/strikethrough.rs created+20
...@@ -0,0 +1,20 @@
1//! Strikethrough syntax (like `~~this~~`)
2use crate::generics::inline::emph_pair;
3use crate::{MarkdownIt, Node, NodeValue, Renderer};
4
5#[derive(Debug)]
6pub struct Strikethrough {
7 pub marker: char
8}
9
10impl NodeValue for Strikethrough {
11 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
12 fmt.open("s", &node.attrs);
13 fmt.contents(&node.children);
14 fmt.close("s");
15 }
16}
17
18pub fn add(md: &mut MarkdownIt) {
19 emph_pair::add_with::<'~', 2, true>(md, || Node::new(Strikethrough { marker: '~' }));
20}
crates/markdown-it/src/plugins/extra/syntect.rs created+74
...@@ -0,0 +1,74 @@
1//! Syntax highlighting for code blocks
2use syntect::highlighting::ThemeSet;
3use syntect::html::highlighted_html_for_string;
4use syntect::parsing::SyntaxSet;
5
6use crate::parser::core::CoreRule;
7use crate::parser::extset::MarkdownItExt;
8use crate::plugins::cmark::block::code::CodeBlock;
9use crate::plugins::cmark::block::fence::CodeFence;
10use crate::{MarkdownIt, Node, NodeValue, Renderer};
11
12#[derive(Debug)]
13pub struct SyntectSnippet {
14 pub html: String,
15}
16
17impl NodeValue for SyntectSnippet {
18 fn render(&self, _: &Node, fmt: &mut dyn Renderer) {
19 fmt.text_raw(&self.html);
20 }
21}
22
23#[derive(Debug, Clone, Copy)]
24struct SyntectSettings(&'static str);
25impl MarkdownItExt for SyntectSettings {}
26
27impl Default for SyntectSettings {
28 fn default() -> Self {
29 Self("InspiredGitHub")
30 }
31}
32
33pub fn add(md: &mut MarkdownIt) {
34 md.add_rule::<SyntectRule>();
35}
36
37pub fn set_theme(md: &mut MarkdownIt, theme: &'static str) {
38 md.ext.insert(SyntectSettings(theme));
39}
40
41pub struct SyntectRule;
42impl CoreRule for SyntectRule {
43 fn run(root: &mut Node, md: &MarkdownIt) {
44 let ss = SyntaxSet::load_defaults_newlines();
45 let ts = ThemeSet::load_defaults();
46 let theme = &ts.themes[md.ext.get::<SyntectSettings>().copied().unwrap_or_default().0];
47
48 root.walk_mut(|node, _| {
49 let mut content = None;
50 let mut language = None;
51
52 if let Some(data) = node.cast::<CodeBlock>() {
53 content = Some(&data.content);
54 } else if let Some(data) = node.cast::<CodeFence>() {
55 language = Some(data.info.clone());
56 content = Some(&data.content);
57 }
58
59 if let Some(content) = content {
60 let mut syntax = None;
61 if let Some(language) = language {
62 syntax = ss.find_syntax_by_token(&language);
63 }
64 let syntax = syntax.unwrap_or_else(|| ss.find_syntax_plain_text());
65
66 let html = highlighted_html_for_string(content, &ss, syntax, theme);
67
68 if let Ok(html) = html {
69 node.replace(SyntectSnippet { html });
70 }
71 }
72 });
73 }
74}
crates/markdown-it/src/plugins/extra/tables.rs created+456
...@@ -0,0 +1,456 @@
1//! GFM tables
2//!
3//! <https://github.github.com/gfm/#tables-extension->
4use crate::common::sourcemap::SourcePos;
5use crate::parser::block::{BlockRule, BlockState};
6use crate::parser::extset::RenderExt;
7use crate::parser::inline::InlineRoot;
8use crate::plugins::cmark::block::heading::HeadingScanner;
9use crate::plugins::cmark::block::list::ListScanner;
10use crate::{MarkdownIt, Node, NodeValue, Renderer};
11
12#[derive(Debug)]
13pub struct Table {
14 pub alignments: Vec<ColumnAlignment>,
15}
16
17impl NodeValue for Table {
18 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
19 let old_context = fmt.ext().remove::<TableRenderContext>();
20 fmt.ext().insert(TableRenderContext { head: false, alignments: self.alignments.clone(), index: 0 });
21
22 fmt.cr();
23 fmt.open("table", &node.attrs);
24 fmt.cr();
25 fmt.contents(&node.children);
26 fmt.cr();
27 fmt.close("table");
28 fmt.cr();
29
30 old_context.map(|ctx| fmt.ext().insert(ctx));
31 }
32}
33
34#[derive(Debug, Default)]
35pub struct TableRenderContext {
36 pub head: bool,
37 pub index: usize,
38 pub alignments: Vec<ColumnAlignment>,
39}
40
41impl RenderExt for TableRenderContext {}
42
43#[derive(Debug)]
44pub struct TableHead;
45
46impl NodeValue for TableHead {
47 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
48 let ctx = fmt.ext().get_or_insert_default::<TableRenderContext>();
49 ctx.head = true;
50
51 fmt.cr();
52 fmt.open("thead", &node.attrs);
53 fmt.cr();
54 fmt.contents(&node.children);
55 fmt.cr();
56 fmt.close("thead");
57 fmt.cr();
58
59 let ctx = fmt.ext().get_or_insert_default::<TableRenderContext>();
60 ctx.head = false;
61 }
62}
63
64#[derive(Debug)]
65pub struct TableBody;
66
67impl NodeValue for TableBody {
68 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
69 fmt.cr();
70 fmt.open("tbody", &node.attrs);
71 fmt.cr();
72 fmt.contents(&node.children);
73 fmt.cr();
74 fmt.close("tbody");
75 fmt.cr();
76 }
77}
78
79#[derive(Debug)]
80pub struct TableRow;
81
82impl NodeValue for TableRow {
83 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
84 let ctx = fmt.ext().get_or_insert_default::<TableRenderContext>();
85 ctx.index = 0;
86
87 fmt.cr();
88 fmt.open("tr", &node.attrs);
89 fmt.cr();
90 fmt.contents(&node.children);
91 fmt.cr();
92 fmt.close("tr");
93 fmt.cr();
94 }
95}
96
97#[derive(Debug)]
98pub struct TableCell;
99
100impl NodeValue for TableCell {
101 fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
102 let ctx = fmt.ext().get_or_insert_default::<TableRenderContext>();
103 let tag = if ctx.head { "th" } else { "td" };
104
105 let mut attrs = node.attrs.clone();
106
107 match ctx.alignments.get(ctx.index).copied().unwrap_or_default() {
108 ColumnAlignment::None => (),
109 ColumnAlignment::Left => attrs.push(("style", "text-align:left".to_owned())),
110 ColumnAlignment::Right => attrs.push(("style", "text-align:right".to_owned())),
111 ColumnAlignment::Center => attrs.push(("style", "text-align:center".to_owned())),
112 }
113
114 ctx.index += 1;
115
116 fmt.open(tag, &attrs);
117 fmt.contents(&node.children);
118 fmt.close(tag);
119 fmt.cr();
120 }
121}
122
123pub fn add(md: &mut MarkdownIt) {
124 md.block.add_rule::<TableScanner>()
125 .before::<ListScanner>()
126 .before::<HeadingScanner>();
127}
128
129#[doc(hidden)]
130pub struct TableScanner;
131
132#[derive(Debug)]
133struct RowContent {
134 str: String,
135 srcmap: Vec<(usize, usize)>,
136}
137
138#[derive(Debug, Clone, Copy)]
139pub enum ColumnAlignment {
140 None,
141 Left,
142 Right,
143 Center,
144}
145
146impl Default for ColumnAlignment {
147 fn default() -> Self { Self::None }
148}
149
150impl TableScanner {
151 fn scan_row(line: &str) -> Vec<RowContent> {
152 let mut result = Vec::new();
153 let mut str = String::new();
154 let mut srcmap = vec![(0, 0)];
155 let mut is_escaped = false;
156 let mut is_leading = true;
157
158 for (pos, ch) in line.char_indices() {
159 match ch {
160 ' ' | '\t' if is_leading => {
161 srcmap[0].1 += 1;
162 }
163 '|' => {
164 is_leading = false;
165 if is_escaped {
166 str.push_str(&line[srcmap.last().unwrap().1..pos-1]);
167 srcmap.push((str.len(), pos));
168 } else {
169 str.push_str(&line[srcmap.last().unwrap().1..pos]);
170 result.push(RowContent {
171 str: std::mem::take(&mut str),
172 srcmap: std::mem::take(&mut srcmap),
173 });
174 srcmap = vec![(0, pos + 1)];
175 is_escaped = false;
176 is_leading = true;
177 }
178 }
179 '\\' => {
180 is_leading = false;
181 is_escaped = true;
182 }
183 _ => {
184 is_leading = false;
185 is_escaped = false;
186 }
187 }
188 }
189
190 str.push_str(&line[srcmap.last().unwrap().1..]);
191 result.push(RowContent {
192 str,
193 srcmap,
194 });
195
196 // trim trailing spaces
197 for content in result.iter_mut() {
198 while content.str.ends_with([ ' ', '\t' ]) {
199 content.str.pop();
200 }
201 }
202
203 // remove last cell if empty
204 if let Some(RowContent { str, srcmap: _ }) = result.last() {
205 if str.is_empty() { result.pop(); }
206 }
207
208 // remove first cell if empty
209 if let Some(RowContent { str, srcmap: _ }) = result.first() {
210 if str.is_empty() { result.remove(0); }
211 }
212
213 result
214 }
215
216 fn scan_alignment_row(line: &str) -> Option<Vec<ColumnAlignment>> {
217 // quick check second line, only allow :-| and spaces
218 // (this is for performance only)
219 let mut has_delimiter = false;
220 for ch in line.chars() {
221 match ch {
222 '|'| ':' => { has_delimiter = true },
223 '-' | ' ' | '\t' => (),
224 _ => return None,
225 }
226 }
227 if !has_delimiter { return None; }
228
229 // if first character is '-', then second character must not be a space
230 // (due to parsing ambiguity with list)
231 if line.starts_with("- ") { return None; }
232
233 let mut result = Vec::new();
234
235 for RowContent { str, srcmap: _ } in Self::scan_row(line) {
236 let mut alignment : u8 = 0;
237 let mut cell = str.as_str();
238
239 if cell.starts_with(':') {
240 alignment |= 1;
241 cell = &cell[1..];
242 }
243
244 if cell.ends_with(':') {
245 alignment |= 2;
246 cell = &cell[..cell.len()-1];
247 }
248
249 // only allow '-----' in the remainder
250 if cell.is_empty() || cell.contains(|c| c != '-') {
251 return None;
252 }
253
254 result.push(match alignment {
255 0 => ColumnAlignment::None,
256 1 => ColumnAlignment::Left,
257 2 => ColumnAlignment::Right,
258 3 => ColumnAlignment::Center,
259 _ => unreachable!(),
260 });
261 }
262
263 Some(result)
264 }
265
266 fn scan_header(state: &BlockState) -> Option<(Vec<RowContent>, Vec<ColumnAlignment>)> {
267 // should have at least two lines
268 if state.line + 2 > state.line_max { return None; }
269
270 if state.line_indent(state.line) >= state.md.max_indent { return None; }
271
272 let next_line = state.line + 1;
273 if state.line_indent(next_line) < 0 { return None; }
274
275 if state.line_indent(next_line) >= state.md.max_indent { return None; }
276
277 let alignments = Self::scan_alignment_row(state.get_line(next_line))?;
278 let header_row = Self::scan_row(state.get_line(state.line));
279
280 // header row must match the delimiter row in the number of cells
281 if header_row.len() != alignments.len() {
282 return None;
283 }
284
285 // table without any columns is not a table, see markdown-it#724
286 if header_row.is_empty() {
287 return None;
288 }
289
290 Some(( header_row, alignments ))
291 }
292}
293
294impl BlockRule for TableScanner {
295 fn check(state: &mut BlockState) -> Option<()> {
296 if state.node.is::<TableBody>() { return None; }
297
298 Self::scan_header(state).map(|_| ())
299 }
300
301 fn run(state: &mut BlockState) -> Option<(Node, usize)> {
302 let ( header_row, alignments ) = Self::scan_header(state)?;
303 let table_cell_count = header_row.len();
304 let mut table_node = Node::new(Table { alignments });
305
306 let mut thead_node = Node::new(TableHead);
307 thead_node.srcmap = state.get_map(state.line, state.line + 1);
308
309 let mut row_node = Node::new(TableRow);
310 row_node.srcmap = state.get_map(state.line, state.line);
311
312 fn add_cell(row_node: &mut Node, cell: String, srcmap: Vec<(usize, usize)>) {
313 let mut cell_node = Node::new(TableCell);
314 let (start, _) = row_node.srcmap.unwrap().get_byte_offsets();
315 cell_node.srcmap = Some(SourcePos::new(
316 start + srcmap.first().unwrap().1,
317 start + srcmap.last().unwrap().1 + cell.len() - srcmap.last().unwrap().0,
318 ));
319 if !cell.is_empty() {
320 let mapping = srcmap.into_iter().map(|(dstpos, srcpos)| (dstpos, srcpos + start)).collect();
321 cell_node.children.push(Node::new(InlineRoot::new(cell, mapping)));
322 }
323 row_node.children.push(cell_node);
324 }
325
326 for RowContent { str: cell, srcmap } in header_row {
327 add_cell(&mut row_node, cell, srcmap);
328 }
329
330 thead_node.children.push(row_node);
331 table_node.children.push(thead_node);
332
333 let tbody_node = Node::new(TableBody);
334 let old_node = std::mem::replace(&mut state.node, tbody_node);
335
336 //
337 // Iterate table rows
338 //
339
340 let start_line = state.line;
341 state.line += 2;
342
343 while state.line < state.line_max {
344 //
345 // Try to check if table is terminated or continued.
346 //
347 if state.line_indent(state.line) < 0 { break; }
348
349 if state.line_indent(state.line) >= state.md.max_indent { break; }
350
351 // stop if the line is empty
352 if state.is_empty(state.line) { break; }
353
354 // fail if terminating block found
355 if state.test_rules_at_line() { break; }
356
357 let mut row_node = Node::new(TableRow);
358 row_node.srcmap = state.get_map(state.line, state.line);
359 let line = state.get_line(state.line);
360
361 let mut body_row = Self::scan_row(line);
362 let mut end_of_line = RowContent { str: String::new(), srcmap: vec![(0, line.len())] };
363
364 for index in 0..table_cell_count {
365 let RowContent { str: cell, srcmap } = body_row.get_mut(index).unwrap_or(&mut end_of_line);
366 add_cell(&mut row_node, cell.clone(), srcmap.clone());
367 }
368
369 state.node.children.push(row_node);
370 state.line += 1;
371 }
372
373 let mut tbody_node = std::mem::replace(&mut state.node, old_node);
374
375 if !tbody_node.children.is_empty() {
376 tbody_node.srcmap = state.get_map(start_line + 2, state.line - 1);
377 table_node.children.push(tbody_node);
378 }
379
380 let line_count = state.line - start_line;
381 state.line = start_line;
382 Some((table_node, line_count))
383 }
384}
385
386
387#[cfg(test)]
388mod tests {
389 use super::TableScanner;
390
391 #[test]
392 fn should_split_cells() {
393 assert_eq!(TableScanner::scan_row("").len(), 0);
394 assert_eq!(TableScanner::scan_row("a").len(), 1);
395 assert_eq!(TableScanner::scan_row("a | b").len(), 2);
396 assert_eq!(TableScanner::scan_row("a | b | c").len(), 3);
397 }
398
399 #[test]
400 fn should_ignore_leading_trailing_empty_cells() {
401 assert_eq!(TableScanner::scan_row("foo | bar").len(), 2);
402 assert_eq!(TableScanner::scan_row("foo | bar |").len(), 2);
403 assert_eq!(TableScanner::scan_row("| foo | bar").len(), 2);
404 assert_eq!(TableScanner::scan_row("| foo | bar |").len(), 2);
405 assert_eq!(TableScanner::scan_row("| | foo | bar | |").len(), 4);
406 assert_eq!(TableScanner::scan_row("|").len(), 0);
407 assert_eq!(TableScanner::scan_row("||").len(), 1);
408 }
409
410 #[test]
411 fn should_trim_cell_content() {
412 assert_eq!(TableScanner::scan_row("|foo|")[0].str, "foo");
413 assert_eq!(TableScanner::scan_row("| foo |")[0].str, "foo");
414 assert_eq!(TableScanner::scan_row("|\tfoo\t|")[0].str, "foo");
415 assert_eq!(TableScanner::scan_row("| \t foo \t |")[0].str, "foo");
416 }
417
418 #[test]
419 fn should_process_backslash_escapes() {
420 assert_eq!(TableScanner::scan_row(r#"| foo\bar |"#)[0].str, r#"foo\bar"#);
421 assert_eq!(TableScanner::scan_row(r#"| foo\|bar |"#)[0].str, r#"foo|bar"#);
422 assert_eq!(TableScanner::scan_row(r#"| foo\\|bar |"#)[0].str, r#"foo\|bar"#);
423 assert_eq!(TableScanner::scan_row(r#"| foo\\\|bar |"#)[0].str, r#"foo\\|bar"#);
424 assert_eq!(TableScanner::scan_row(r#"| foo\\\\|bar |"#)[0].str, r#"foo\\\|bar"#);
425 }
426
427 #[test]
428 fn should_trim_cell_content_srcmaps() {
429 let row = TableScanner::scan_row("| foo | \tbar\t |");
430 assert_eq!(row[0].str, "foo");
431 assert_eq!(row[0].srcmap, vec![(0, 2)]);
432 assert_eq!(row[1].str, "bar");
433 assert_eq!(row[1].srcmap, vec![(0, 9)]);
434 }
435
436 #[test]
437 fn should_process_backslash_escapes_srcmaps() {
438 let row = TableScanner::scan_row(r#"| foo\\|bar\\\|baz\ |"#);
439 assert_eq!(row[0].str, r#"foo\|bar\\|baz\"#);
440 assert_eq!(row[0].srcmap, vec![(0, 3), (4, 8), (10, 15)]);
441 }
442
443 #[test]
444 fn require_pipe_or_colon_in_align_row() {
445 let md = &mut crate::MarkdownIt::new();
446 crate::plugins::extra::tables::add(md);
447 let html = md.parse("foo\n---\nbar").render();
448 assert_eq!(html.trim(), "foo\n---\nbar");
449 let html = md.parse("|foo\n---\nbar").render();
450 assert_eq!(html.trim(), "|foo\n---\nbar");
451 let html = md.parse("foo\n|---\nbar").render();
452 assert!(html.trim().starts_with("<table"));
453 let html = md.parse("foo\n:---\nbar").render();
454 assert!(html.trim().starts_with("<table"));
455 }
456}
crates/markdown-it/src/plugins/extra/typographer.rs created+124
...@@ -0,0 +1,124 @@
1//! Common textual replacements for dashes, ©, ™, …
2//!
3//! **Note:** Since this plugin is most useful with smart-quotes, which is not
4//! currently implemented, this plugin is _not_ enabled by default when using
5//! `plugins::extra::add`. You will have to enable it separately:
6//!
7//! ```rust
8//! let md = &mut markdown_it::MarkdownIt::new();
9//! markdown_it::plugins::cmark::add(md);
10//! markdown_it::plugins::extra::add(md);
11//! markdown_it::plugins::extra::typographer::add(md);
12//!
13//! let html = md.parse("Hello world!.... This is the Right Way(TM) to markdown!!!!!").render();
14//! assert_eq!(html.trim(), r#"<p>Hello world!.. This is the Right Way™ to markdown!!!</p>"#);
15//! ```
16//! In summary, these are the replacements that will be made when using this:
17//!
18//! ## Typography
19//!
20//! - Repeated dots (`...`) to ellipsis (`…`)
21//! except `?...` and `!...` which become `?..` and `!..` respectively
22//! - `+-` to `±`
23//! - Don't repeat `?` and `!` more than 3 times: `???`
24//! - De-duplicate commas
25//! - em and en dashes: `--` to `–` and `---` to `—`
26//!
27//! ## Common symbols (case insensitive)
28//!
29//! - Copyright: `(c)` to `©`
30//! - Reserved: `(r)` to `®`
31//! - Trademark: `(tm)` to `™`
32
33use once_cell::sync::Lazy;
34use regex::Regex;
35use std::borrow::Cow;
36
37use crate::parser::core::CoreRule;
38use crate::parser::inline::Text;
39use crate::{MarkdownIt, Node};
40
41static REPLACEMENTS: Lazy<Box<[(Regex, &'static str)]>> = Lazy::new(|| {
42 Box::new([
43 (Regex::new(r"\+-").unwrap(), "±"),
44 (Regex::new(r"\.{2,}").unwrap(), "…"),
45 (Regex::new(r"([?!])…").unwrap(), "$1.."),
46 (Regex::new(r"([?!]){4,}").unwrap(), "$1$1$1"),
47 (Regex::new(r",{2,}").unwrap(), ","),
48 // These look a little different from the JS implementation because the
49 // regex crate doesn't support look-behind and look-ahead patterns
50 (
51 Regex::new(r"(?m)(?P<pre>^|[^-])(?P<dash>---)(?P<post>[^-]|$)").unwrap(),
52 "$pre\u{2014}$post",
53 ),
54 (
55 Regex::new(r"(?m)(?P<pre>^|\s)(?P<dash>--)(?P<post>\s|$)").unwrap(),
56 "$pre\u{2013}$post",
57 ),
58 (
59 Regex::new(r"(?m)(?P<pre>^|[^-\s])(?P<dash>--)(?P<post>[^-\s]|$)").unwrap(),
60 "$pre\u{2013}$post",
61 ),
62 ])
63});
64static SCOPED_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?i)\((c|tm|r)\)").unwrap());
65static RARE_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"\+-|\.\.|\?\?\?\?|!!!!|,,|--").unwrap());
66
67fn replace_abbreviation(input: &str) -> &'static str {
68 match input.to_lowercase().as_str() {
69 "(c)" => "©",
70 "(r)" => "®",
71 "(tm)" => "™",
72 _ => unreachable!("Got invalid abbreviation '{}'", input),
73 }
74}
75
76pub fn add(md: &mut MarkdownIt) {
77 md.add_rule::<TypographerRule>();
78}
79
80pub struct TypographerRule;
81
82impl CoreRule for TypographerRule {
83 fn run(root: &mut Node, _: &MarkdownIt) {
84 root.walk_mut(|node, _| {
85 let Some(text_node) = node.cast_mut::<Text>() else { return; };
86
87 if SCOPED_RE.is_match(&text_node.content) {
88 text_node.content = SCOPED_RE
89 .replace_all(&text_node.content, |caps: &regex::Captures| {
90 replace_abbreviation(caps.get(0).unwrap().as_str())
91 })
92 .to_string();
93 }
94 if RARE_RE.is_match(&text_node.content) {
95 let mut result = Cow::Borrowed(text_node.content.as_str());
96
97 for (pattern, replacement) in REPLACEMENTS.iter() {
98 if let Cow::Owned(s) = pattern.replace_all(&result, *replacement) {
99 result = Cow::Owned(s);
100
101 // This is a bit unfortunate but since we can't use
102 // look-ahead and look-behind patterns in the dash
103 // replacements, the preceding and following
104 // characters (pre and post in the patterns) become
105 // part of the match. So a string like "bla-- --foo"
106 // would create two *overlapping* matches, "a-- "
107 // and " --f". But replace_all only replaces
108 // non-overlapping matches. So we can't do this in
109 // one single replacement. My only consolation here
110 // is that this won't happen very often in practice,
111 // and that it cost us "only" one extra call.
112 if let Cow::Owned(s) = pattern.replace_all(&result, *replacement) {
113 result = Cow::Owned(s);
114 }
115 }
116 }
117
118 if let Cow::Owned(s) = result {
119 text_node.content = s;
120 }
121 }
122 });
123 }
124}
crates/markdown-it/src/plugins/html/html_block.rs created+151
...@@ -0,0 +1,151 @@
1//! HTML block syntax from CommonMark
2//!
3//! <https://spec.commonmark.org/0.30/#html-blocks>
4use once_cell::sync::Lazy;
5use regex::Regex;
6
7use super::utils::blocks::*;
8use super::utils::regexps::*;
9use crate::parser::block::{BlockRule, BlockState};
10use crate::{MarkdownIt, Node, NodeValue, Renderer};
11
12#[derive(Debug)]
13pub struct HtmlBlock {
14 pub content: String,
15}
16
17impl NodeValue for HtmlBlock {
18 fn render(&self, _: &Node, fmt: &mut dyn Renderer) {
19 fmt.cr();
20 fmt.text_raw(&self.content);
21 fmt.cr();
22 }
23}
24
25pub fn add(md: &mut MarkdownIt) {
26 md.block.add_rule::<HtmlBlockScanner>();
27}
28
29struct HTMLSequence {
30 open: Regex,
31 close: Regex,
32 can_terminate_paragraph: bool,
33}
34
35impl HTMLSequence {
36 pub fn new(open: Regex, close: Regex, can_terminate_paragraph: bool) -> Self {
37 Self { open, close, can_terminate_paragraph }
38 }
39}
40
41// An array of opening and corresponding closing sequences for html tags,
42// last argument defines whether it can terminate a paragraph or not
43//
44static HTML_SEQUENCES : Lazy<[HTMLSequence; 7]> = Lazy::new(|| {
45 let block_names = HTML_BLOCKS.join("|");
46 let open_close_tag_re = HTML_OPEN_CLOSE_TAG_RE.as_str();
47
48 [
49 HTMLSequence::new(
50 Regex::new(r#"(?i)^<(script|pre|style|textarea)(\s|>|$)"#).unwrap(),
51 Regex::new(r#"(?i)</(script|pre|style|textarea)>"#).unwrap(),
52 true
53 ),
54
55 HTMLSequence::new(
56 Regex::new(r#"^<!--"#).unwrap(),
57 Regex::new(r#"-->"#).unwrap(),
58 true
59 ),
60
61 HTMLSequence::new(
62 Regex::new(r#"^<\?"#).unwrap(),
63 Regex::new(r#"\?>"#).unwrap(),
64 true
65 ),
66
67 HTMLSequence::new(
68 Regex::new(r#"^<![A-Z]"#).unwrap(),
69 Regex::new(r#">"#).unwrap(),
70 true
71 ),
72
73 HTMLSequence::new(
74 Regex::new(r#"^<!\[CDATA\["#).unwrap(),
75 Regex::new(r#"\]\]>"#).unwrap(),
76 true
77 ),
78
79 HTMLSequence::new(
80 Regex::new(&format!("(?i)^</?({block_names})(\\s|/?>|$)")).unwrap(),
81 Regex::new(r#"^$"#).unwrap(),
82 true
83 ),
84
85 HTMLSequence::new(
86 Regex::new(&format!("{open_close_tag_re}\\s*$")).unwrap(),
87 Regex::new(r#"^$"#).unwrap(),
88 false
89 ),
90 ]
91});
92
93#[doc(hidden)]
94pub struct HtmlBlockScanner;
95
96impl HtmlBlockScanner {
97 fn get_sequence(state: &mut BlockState) -> Option<&'static HTMLSequence> {
98
99 if state.line_indent(state.line) >= state.md.max_indent { return None; }
100
101 let line_text = state.get_line(state.line);
102 let Some('<') = line_text.chars().next() else { return None; };
103
104 let mut sequence = None;
105 for seq in HTML_SEQUENCES.iter() {
106 if seq.open.is_match(line_text) {
107 sequence = Some(seq);
108 break;
109 }
110 }
111
112 sequence
113 }
114}
115
116impl BlockRule for HtmlBlockScanner {
117 fn check(state: &mut BlockState) -> Option<()> {
118 let sequence = Self::get_sequence(state)?;
119 if !sequence.can_terminate_paragraph { return None; }
120 Some(())
121 }
122
123 fn run(state: &mut BlockState) -> Option<(Node, usize)> {
124 let sequence = Self::get_sequence(state)?;
125
126 let line_text = state.get_line(state.line);
127 let start_line = state.line;
128 let mut next_line = state.line + 1;
129
130 // If we are here - we detected HTML block.
131 // Let's roll down till block end.
132 if !sequence.close.is_match(line_text) {
133 while next_line < state.line_max {
134 if state.line_indent(next_line) < 0 { break; }
135
136 let line_text = state.get_line(next_line);
137
138 if sequence.close.is_match(line_text) {
139 if !line_text.is_empty() { next_line += 1; }
140 break;
141 }
142
143 next_line += 1;
144 }
145 }
146
147 let (content, _) = state.get_lines(start_line, next_line, state.blk_indent, true);
148 let node = Node::new(HtmlBlock { content });
149 Some((node, next_line - state.line))
150 }
151}
crates/markdown-it/src/plugins/html/html_inline.rs created+50
...@@ -0,0 +1,50 @@
1//! HTML inline syntax from CommonMark
2//!
3//! <https://spec.commonmark.org/0.30/#raw-html>
4use super::utils::regexps::*;
5use crate::parser::inline::{InlineRule, InlineState};
6use crate::{MarkdownIt, Node, NodeValue, Renderer};
7
8#[derive(Debug)]
9pub struct HtmlInline {
10 pub content: String,
11}
12
13impl NodeValue for HtmlInline {
14 fn render(&self, _: &Node, fmt: &mut dyn Renderer) {
15 fmt.text_raw(&self.content);
16 }
17}
18
19pub fn add(md: &mut MarkdownIt) {
20 md.inline.add_rule::<HtmlInlineScanner>();
21}
22
23#[doc(hidden)]
24pub struct HtmlInlineScanner;
25impl InlineRule for HtmlInlineScanner {
26 const MARKER: char = '<';
27
28 fn run(state: &mut InlineState) -> Option<(Node, usize)> {
29 // Check start
30 let mut chars = state.src[state.pos..state.pos_max].chars();
31 if chars.next().unwrap() != '<' { return None; }
32
33 // Quick fail on second char
34 let Some('!' | '?' | '/' | 'A'..='Z' | 'a'..='z') = chars.next() else { return None; };
35
36 let capture = HTML_TAG_RE.captures(&state.src[state.pos..state.pos_max])?.get(0).unwrap().as_str();
37 let capture_len = capture.len();
38
39 let content = capture.to_owned();
40
41 if HTML_LINK_OPEN.is_match(&content) {
42 state.link_level += 1;
43 } else if HTML_LINK_CLOSE.is_match(&content) {
44 state.link_level -= 1;
45 }
46
47 let node = Node::new(HtmlInline { content });
48 Some((node, capture_len))
49 }
50}
crates/markdown-it/src/plugins/html/mod.rs created+29
...@@ -0,0 +1,29 @@
1//! Raw html syntax (block and inline), part of CommonMark standard.
2//!
3//! This feature is separated from cmark because it is unsafe to enable by
4//! default (due to lack of any kind of html sanitization).
5//!
6//! You can enable it if you're:
7//! - looking for strict CommonMark compatibility
8//! - only have trusted input (i.e. writing markdown yourself)
9//! - or took some care to sanitize html yourself
10//!
11//! ```rust
12//! let md = &mut markdown_it::MarkdownIt::new();
13//! markdown_it::plugins::cmark::add(md);
14//! markdown_it::plugins::html::add(md);
15//!
16//! let html = md.parse("hello<br>world").render();
17//! assert_eq!(html.trim(), r#"<p>hello<br>world</p>"#);
18//! ```
19
20pub mod html_block;
21pub mod html_inline;
22mod utils;
23
24use crate::MarkdownIt;
25
26pub fn add(md: &mut MarkdownIt) {
27 html_inline::add(md);
28 html_block::add(md);
29}
crates/markdown-it/src/plugins/html/utils/blocks.rs created+68
...@@ -0,0 +1,68 @@
1//! List of valid html blocks names, according to commonmark spec
2//! http://jgm.github.io/CommonMark/spec.html#html-blocks
3//!
4
5pub const HTML_BLOCKS: [&str; 62] = [
6 "address",
7 "article",
8 "aside",
9 "base",
10 "basefont",
11 "blockquote",
12 "body",
13 "caption",
14 "center",
15 "col",
16 "colgroup",
17 "dd",
18 "details",
19 "dialog",
20 "dir",
21 "div",
22 "dl",
23 "dt",
24 "fieldset",
25 "figcaption",
26 "figure",
27 "footer",
28 "form",
29 "frame",
30 "frameset",
31 "h1",
32 "h2",
33 "h3",
34 "h4",
35 "h5",
36 "h6",
37 "head",
38 "header",
39 "hr",
40 "html",
41 "iframe",
42 "legend",
43 "li",
44 "link",
45 "main",
46 "menu",
47 "menuitem",
48 "nav",
49 "noframes",
50 "ol",
51 "optgroup",
52 "option",
53 "p",
54 "param",
55 "section",
56 "source",
57 "summary",
58 "table",
59 "tbody",
60 "td",
61 "tfoot",
62 "th",
63 "thead",
64 "title",
65 "tr",
66 "track",
67 "ul",
68];
crates/markdown-it/src/plugins/html/utils/mod.rs created+2
...@@ -0,0 +1,2 @@
1pub mod blocks;
2pub mod regexps;
crates/markdown-it/src/plugins/html/utils/regexps.rs created+46
...@@ -0,0 +1,46 @@
1//! Regexps to match html elements
2//!
3#![allow(non_upper_case_globals)]
4use const_format::formatcp;
5use once_cell::sync::Lazy;
6use regex::Regex;
7
8const attr_name : &str = r#"[a-zA-Z_:][a-zA-Z0-9:._-]*"#;
9
10const unquoted : &str = r#"[^"'=<>`\x00-\x20]+"#;
11const single_quoted : &str = r#"'[^']*'"#;
12const double_quoted : &str = r#""[^"]*""#;
13
14const attr_value : &str = formatcp!("(?:{unquoted}|{single_quoted}|{double_quoted})");
15
16const attribute : &str = formatcp!("(?:\\s+{attr_name}(?:\\s*=\\s*{attr_value})?)");
17
18const open_tag : &str = formatcp!("<[A-Za-z][A-Za-z0-9\\-]*{attribute}*\\s*/?>");
19
20const close_tag : &str = r#"</[A-Za-z][A-Za-z0-9\-]*\s*>"#;
21const comment : &str = r#"<!---->|<!--(?:-?[^>-])(?:-?[^-])*-->"#;
22const processing : &str = r#"<[?][\s\S]*?[?]>"#;
23const declaration : &str = r#"<![A-Z]+\s+[^>]*>"#;
24const cdata : &str = r#"<!\[CDATA\[[\s\S]*?\]\]>"#;
25
26#[allow(clippy::double_parens)]
27pub static HTML_TAG_RE : Lazy<Regex> = Lazy::new(|| {
28 Regex::new(
29 formatcp!("^(?:{open_tag}|{close_tag}|{comment}|{processing}|{declaration}|{cdata})")
30 ).unwrap()
31});
32
33#[allow(clippy::double_parens)]
34pub static HTML_OPEN_CLOSE_TAG_RE : Lazy<Regex> = Lazy::new(|| {
35 Regex::new(
36 formatcp!("^(?:{open_tag}|{close_tag})")
37 ).unwrap()
38});
39
40pub static HTML_LINK_OPEN : Lazy<Regex> = Lazy::new(|| {
41 Regex::new(r#"^<a[>\s]"#).unwrap()
42});
43
44pub static HTML_LINK_CLOSE : Lazy<Regex> = Lazy::new(|| {
45 Regex::new(r#"^</a\s*>"#).unwrap()
46});
crates/markdown-it/src/plugins/mod.rs created+20
...@@ -0,0 +1,20 @@
1//! Ready-to-use plugins. Everything, including basic markdown syntax, is a plugin.
2//!
3//! This library is made to be as extensible as possible. In order to ensure that
4//! you can write your own markdown syntax of any arbitrary complexity,
5//! CommonMark syntax itself is made into a plugin (`cmark`), which you can use
6//! as an example of how to write your own.
7//!
8//! Add each plugin you need by invoking `add` function like this:
9//! ```rust
10//! let md = &mut markdown_it::MarkdownIt::new();
11//! markdown_it::plugins::cmark::add(md);
12//! markdown_it::plugins::extra::add(md);
13//! markdown_it::plugins::html::add(md);
14//! markdown_it::plugins::sourcepos::add(md);
15//! // ...
16//! ```
17pub mod cmark;
18pub mod extra;
19pub mod html;
20pub mod sourcepos;
crates/markdown-it/src/plugins/sourcepos.rs created+52
...@@ -0,0 +1,52 @@
1//! Add source mapping to resulting HTML, looks like this: `<stuff data-sourcepos="1:1-2:3">`.
2//! ```rust
3//! let md = &mut markdown_it::MarkdownIt::new();
4//! markdown_it::plugins::cmark::add(md);
5//! markdown_it::plugins::sourcepos::add(md);
6//!
7//! let html = md.parse("# hello").render();
8//! assert_eq!(html.trim(), r#"<h1 data-sourcepos="1:1-1:7">hello</h1>"#);
9//! ```
10use crate::common::sourcemap::SourceWithLineStarts;
11use crate::parser::block::builtin::BlockParserRule;
12use crate::parser::core::{CoreRule, Root};
13use crate::parser::inline::builtin::InlineParserRule;
14use crate::{MarkdownIt, Node};
15
16pub fn add(md: &mut MarkdownIt) {
17 md.add_rule::<SyntaxPosRule>()
18 .after::<BlockParserRule>()
19 .after::<InlineParserRule>();
20}
21
22#[doc(hidden)]
23pub struct SyntaxPosRule;
24impl CoreRule for SyntaxPosRule {
25 fn run(root: &mut Node, _: &MarkdownIt) {
26 let source = root.cast::<Root>().unwrap().content.as_str();
27 let mapping = SourceWithLineStarts::new(source);
28
29 root.walk_mut(|node, _| {
30 if let Some(map) = node.srcmap {
31 let ((startline, startcol), (endline, endcol)) = map.get_positions(&mapping);
32 node.attrs.push(("data-sourcepos", format!("{}:{}-{}:{}", startline, startcol, endline, endcol)));
33 }
34 });
35 }
36}
37
38
39#[cfg(test)]
40mod tests {
41 #[test]
42 fn header_test() {
43 // same as doctest, keep in sync!
44 // used for code coverage and quicker rust-analyzer hints
45 let md = &mut crate::MarkdownIt::new();
46 crate::plugins::cmark::add(md);
47 crate::plugins::sourcepos::add(md);
48
49 let html = md.parse("# hello").render();
50 assert_eq!(html.trim(), r#"<h1 data-sourcepos="1:1-1:7">hello</h1>"#);
51 }
52}
crates/markdown-it/tests/commonmark.rs created+6557
...@@ -0,0 +1,6557 @@
1
2fn run(input: &str, output: &str) {
3 let output = if output.is_empty() { "".to_owned() } else { output.to_owned() + "\n" };
4 let md = &mut markdown_it::MarkdownIt::new();
5 markdown_it::plugins::cmark::add(md);
6 markdown_it::plugins::html::add(md);
7 let node = md.parse(&(input.to_owned() + "\n"));
8
9 // make sure we have sourcemaps for everything
10 node.walk(|node, _| assert!(node.srcmap.is_some()));
11
12 let result = node.xrender();
13 assert_eq!(result, output);
14
15 // make sure it doesn't crash without trailing \n
16 let _ = md.parse(input.trim_end());
17}
18
19///////////////////////////////////////////////////////////////////////////
20// TESTGEN: fixtures/commonmark/good.txt
21#[rustfmt::skip]
22mod fixtures_commonmark_good_txt {
23use super::run;
24// this part of the file is auto-generated
25// don't edit it, otherwise your changes might be lost
26#[test]
27fn src_line_355() {
28 let input = "\tfoo\tbaz\t\tbim";
29 let output = "<pre><code>foo\tbaz\t\tbim
30</code></pre>";
31 run(input, output);
32}
33
34#[test]
35fn src_line_362() {
36 let input = " \tfoo\tbaz\t\tbim";
37 let output = "<pre><code>foo\tbaz\t\tbim
38</code></pre>";
39 run(input, output);
40}
41
42#[test]
43fn src_line_369() {
44 let input = " a\ta
45 ὐ\ta";
46 let output = "<pre><code>a\ta
47ὐ\ta
48</code></pre>";
49 run(input, output);
50}
51
52#[test]
53fn src_line_382() {
54 let input = " - foo
55
56\tbar";
57 let output = r#"<ul>
58<li>
59<p>foo</p>
60<p>bar</p>
61</li>
62</ul>"#;
63 run(input, output);
64}
65
66#[test]
67fn src_line_395() {
68 let input = "- foo
69
70\t\tbar";
71 let output = r#"<ul>
72<li>
73<p>foo</p>
74<pre><code> bar
75</code></pre>
76</li>
77</ul>"#;
78 run(input, output);
79}
80
81#[test]
82fn src_line_418() {
83 let input = ">\t\tfoo";
84 let output = r#"<blockquote>
85<pre><code> foo
86</code></pre>
87</blockquote>"#;
88 run(input, output);
89}
90
91#[test]
92fn src_line_427() {
93 let input = "-\t\tfoo";
94 let output = r#"<ul>
95<li>
96<pre><code> foo
97</code></pre>
98</li>
99</ul>"#;
100 run(input, output);
101}
102
103#[test]
104fn src_line_439() {
105 let input = " foo
106\tbar";
107 let output = r#"<pre><code>foo
108bar
109</code></pre>"#;
110 run(input, output);
111}
112
113#[test]
114fn src_line_448() {
115 let input = " - foo
116 - bar
117\t - baz";
118 let output = r#"<ul>
119<li>foo
120<ul>
121<li>bar
122<ul>
123<li>baz</li>
124</ul>
125</li>
126</ul>
127</li>
128</ul>"#;
129 run(input, output);
130}
131
132#[test]
133fn src_line_466() {
134 let input = "#\tFoo";
135 let output = r#"<h1>Foo</h1>"#;
136 run(input, output);
137}
138
139#[test]
140fn src_line_472() {
141 let input = "*\t*\t*\t";
142 let output = r#"<hr />"#;
143 run(input, output);
144}
145
146#[test]
147fn src_line_489() {
148 let input = r#"\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~"#;
149 let output = r#"<p>!&quot;#$%&amp;'()*+,-./:;&lt;=&gt;?@[\]^_`{|}~</p>"#;
150 run(input, output);
151}
152
153#[test]
154fn src_line_499() {
155 let input = "\\\t\\A\\a\\ \\3\\φ\\«";
156 let output = "<p>\\\t\\A\\a\\ \\3\\φ\\«</p>";
157 run(input, output);
158}
159
160#[test]
161fn src_line_509() {
162 let input = r#"\*not emphasized*
163\<br/> not a tag
164\[not a link](/foo)
165\`not code`
1661\. not a list
167\* not a list
168\# not a heading
169\[foo]: /url "not a reference"
170\&ouml; not a character entity"#;
171 let output = r#"<p>*not emphasized*
172&lt;br/&gt; not a tag
173[not a link](/foo)
174`not code`
1751. not a list
176* not a list
177# not a heading
178[foo]: /url &quot;not a reference&quot;
179&amp;ouml; not a character entity</p>"#;
180 run(input, output);
181}
182
183#[test]
184fn src_line_534() {
185 let input = r#"\\*emphasis*"#;
186 let output = r#"<p>\<em>emphasis</em></p>"#;
187 run(input, output);
188}
189
190#[test]
191fn src_line_543() {
192 let input = r#"foo\
193bar"#;
194 let output = r#"<p>foo<br />
195bar</p>"#;
196 run(input, output);
197}
198
199#[test]
200fn src_line_555() {
201 let input = r#"`` \[\` ``"#;
202 let output = r#"<p><code>\[\`</code></p>"#;
203 run(input, output);
204}
205
206#[test]
207fn src_line_562() {
208 let input = r#" \[\]"#;
209 let output = r#"<pre><code>\[\]
210</code></pre>"#;
211 run(input, output);
212}
213
214#[test]
215fn src_line_570() {
216 let input = r#"~~~
217\[\]
218~~~"#;
219 let output = r#"<pre><code>\[\]
220</code></pre>"#;
221 run(input, output);
222}
223
224#[test]
225fn src_line_580() {
226 let input = r#"<http://example.com?find=\*>"#;
227 let output = r#"<p><a href="http://example.com?find=%5C*">http://example.com?find=\*</a></p>"#;
228 run(input, output);
229}
230
231#[test]
232fn src_line_587() {
233 let input = r#"<a href="/bar\/)">"#;
234 let output = r#"<a href="/bar\/)">"#;
235 run(input, output);
236}
237
238#[test]
239fn src_line_597() {
240 let input = r#"[foo](/bar\* "ti\*tle")"#;
241 let output = r#"<p><a href="/bar*" title="ti*tle">foo</a></p>"#;
242 run(input, output);
243}
244
245#[test]
246fn src_line_604() {
247 let input = r#"[foo]
248
249[foo]: /bar\* "ti\*tle""#;
250 let output = r#"<p><a href="/bar*" title="ti*tle">foo</a></p>"#;
251 run(input, output);
252}
253
254#[test]
255fn src_line_613() {
256 let input = r#"``` foo\+bar
257foo
258```"#;
259 let output = r#"<pre><code class="language-foo+bar">foo
260</code></pre>"#;
261 run(input, output);
262}
263
264#[test]
265fn src_line_649() {
266 let input = r#"&nbsp; &amp; &copy; &AElig; &Dcaron;
267&frac34; &HilbertSpace; &DifferentialD;
268&ClockwiseContourIntegral; &ngE;"#;
269 let output = r#"<p>  &amp; © Æ Ď
270¾ ℋ ⅆ
271∲ ≧̸</p>"#;
272 run(input, output);
273}
274
275#[test]
276fn src_line_668() {
277 let input = r#"&#35; &#1234; &#992; &#0;"#;
278 let output = r#"<p># Ӓ Ϡ �</p>"#;
279 run(input, output);
280}
281
282#[test]
283fn src_line_681() {
284 let input = r#"&#X22; &#XD06; &#xcab;"#;
285 let output = r#"<p>&quot; ആ ಫ</p>"#;
286 run(input, output);
287}
288
289#[test]
290fn src_line_690() {
291 let input = r#"&nbsp &x; &#; &#x;
292&#87654321;
293&#abcdef0;
294&ThisIsNotDefined; &hi?;"#;
295 let output = r#"<p>&amp;nbsp &amp;x; &amp;#; &amp;#x;
296&amp;#87654321;
297&amp;#abcdef0;
298&amp;ThisIsNotDefined; &amp;hi?;</p>"#;
299 run(input, output);
300}
301
302#[test]
303fn src_line_707() {
304 let input = r#"&copy"#;
305 let output = r#"<p>&amp;copy</p>"#;
306 run(input, output);
307}
308
309#[test]
310fn src_line_717() {
311 let input = r#"&MadeUpEntity;"#;
312 let output = r#"<p>&amp;MadeUpEntity;</p>"#;
313 run(input, output);
314}
315
316#[test]
317fn src_line_728() {
318 let input = r#"<a href="&ouml;&ouml;.html">"#;
319 let output = r#"<a href="&ouml;&ouml;.html">"#;
320 run(input, output);
321}
322
323#[test]
324fn src_line_735() {
325 let input = r#"[foo](/f&ouml;&ouml; "f&ouml;&ouml;")"#;
326 let output = r#"<p><a href="/f%C3%B6%C3%B6" title="föö">foo</a></p>"#;
327 run(input, output);
328}
329
330#[test]
331fn src_line_742() {
332 let input = r#"[foo]
333
334[foo]: /f&ouml;&ouml; "f&ouml;&ouml;""#;
335 let output = r#"<p><a href="/f%C3%B6%C3%B6" title="föö">foo</a></p>"#;
336 run(input, output);
337}
338
339#[test]
340fn src_line_751() {
341 let input = r#"``` f&ouml;&ouml;
342foo
343```"#;
344 let output = r#"<pre><code class="language-föö">foo
345</code></pre>"#;
346 run(input, output);
347}
348
349#[test]
350fn src_line_764() {
351 let input = r#"`f&ouml;&ouml;`"#;
352 let output = r#"<p><code>f&amp;ouml;&amp;ouml;</code></p>"#;
353 run(input, output);
354}
355
356#[test]
357fn src_line_771() {
358 let input = r#" f&ouml;f&ouml;"#;
359 let output = r#"<pre><code>f&amp;ouml;f&amp;ouml;
360</code></pre>"#;
361 run(input, output);
362}
363
364#[test]
365fn src_line_783() {
366 let input = r#"&#42;foo&#42;
367*foo*"#;
368 let output = r#"<p>*foo*
369<em>foo</em></p>"#;
370 run(input, output);
371}
372
373#[test]
374fn src_line_791() {
375 let input = r#"&#42; foo
376
377* foo"#;
378 let output = r#"<p>* foo</p>
379<ul>
380<li>foo</li>
381</ul>"#;
382 run(input, output);
383}
384
385#[test]
386fn src_line_802() {
387 let input = r#"foo&#10;&#10;bar"#;
388 let output = r#"<p>foo
389
390bar</p>"#;
391 run(input, output);
392}
393
394#[test]
395fn src_line_810() {
396 let input = r#"&#9;foo"#;
397 let output = "<p>\tfoo</p>";
398 run(input, output);
399}
400
401#[test]
402fn src_line_817() {
403 let input = r#"[a](url &quot;tit&quot;)"#;
404 let output = r#"<p>[a](url &quot;tit&quot;)</p>"#;
405 run(input, output);
406}
407
408#[test]
409fn src_line_840() {
410 let input = r#"- `one
411- two`"#;
412 let output = r#"<ul>
413<li>`one</li>
414<li>two`</li>
415</ul>"#;
416 run(input, output);
417}
418
419#[test]
420fn src_line_879() {
421 let input = r#"***
422---
423___"#;
424 let output = r#"<hr />
425<hr />
426<hr />"#;
427 run(input, output);
428}
429
430#[test]
431fn src_line_892() {
432 let input = r#"+++"#;
433 let output = r#"<p>+++</p>"#;
434 run(input, output);
435}
436
437#[test]
438fn src_line_899() {
439 let input = r#"==="#;
440 let output = r#"<p>===</p>"#;
441 run(input, output);
442}
443
444#[test]
445fn src_line_908() {
446 let input = r#"--
447**
448__"#;
449 let output = r#"<p>--
450**
451__</p>"#;
452 run(input, output);
453}
454
455#[test]
456fn src_line_921() {
457 let input = r#" ***
458 ***
459 ***"#;
460 let output = r#"<hr />
461<hr />
462<hr />"#;
463 run(input, output);
464}
465
466#[test]
467fn src_line_934() {
468 let input = r#" ***"#;
469 let output = r#"<pre><code>***
470</code></pre>"#;
471 run(input, output);
472}
473
474#[test]
475fn src_line_942() {
476 let input = r#"Foo
477 ***"#;
478 let output = r#"<p>Foo
479***</p>"#;
480 run(input, output);
481}
482
483#[test]
484fn src_line_953() {
485 let input = r#"_____________________________________"#;
486 let output = r#"<hr />"#;
487 run(input, output);
488}
489
490#[test]
491fn src_line_962() {
492 let input = r#" - - -"#;
493 let output = r#"<hr />"#;
494 run(input, output);
495}
496
497#[test]
498fn src_line_969() {
499 let input = r#" ** * ** * ** * **"#;
500 let output = r#"<hr />"#;
501 run(input, output);
502}
503
504#[test]
505fn src_line_976() {
506 let input = r#"- - - -"#;
507 let output = r#"<hr />"#;
508 run(input, output);
509}
510
511#[test]
512fn src_line_985() {
513 let input = "- - - - \x20";
514 let output = r#"<hr />"#;
515 run(input, output);
516}
517
518#[test]
519fn src_line_994() {
520 let input = r#"_ _ _ _ a
521
522a------
523
524---a---"#;
525 let output = r#"<p>_ _ _ _ a</p>
526<p>a------</p>
527<p>---a---</p>"#;
528 run(input, output);
529}
530
531#[test]
532fn src_line_1010() {
533 let input = r#" *-*"#;
534 let output = r#"<p><em>-</em></p>"#;
535 run(input, output);
536}
537
538#[test]
539fn src_line_1019() {
540 let input = r#"- foo
541***
542- bar"#;
543 let output = r#"<ul>
544<li>foo</li>
545</ul>
546<hr />
547<ul>
548<li>bar</li>
549</ul>"#;
550 run(input, output);
551}
552
553#[test]
554fn src_line_1036() {
555 let input = r#"Foo
556***
557bar"#;
558 let output = r#"<p>Foo</p>
559<hr />
560<p>bar</p>"#;
561 run(input, output);
562}
563
564#[test]
565fn src_line_1053() {
566 let input = r#"Foo
567---
568bar"#;
569 let output = r#"<h2>Foo</h2>
570<p>bar</p>"#;
571 run(input, output);
572}
573
574#[test]
575fn src_line_1066() {
576 let input = r#"* Foo
577* * *
578* Bar"#;
579 let output = r#"<ul>
580<li>Foo</li>
581</ul>
582<hr />
583<ul>
584<li>Bar</li>
585</ul>"#;
586 run(input, output);
587}
588
589#[test]
590fn src_line_1083() {
591 let input = r#"- Foo
592- * * *"#;
593 let output = r#"<ul>
594<li>Foo</li>
595<li>
596<hr />
597</li>
598</ul>"#;
599 run(input, output);
600}
601
602#[test]
603fn src_line_1112() {
604 let input = r#"# foo
605## foo
606### foo
607#### foo
608##### foo
609###### foo"#;
610 let output = r#"<h1>foo</h1>
611<h2>foo</h2>
612<h3>foo</h3>
613<h4>foo</h4>
614<h5>foo</h5>
615<h6>foo</h6>"#;
616 run(input, output);
617}
618
619#[test]
620fn src_line_1131() {
621 let input = r#"####### foo"#;
622 let output = r#"<p>####### foo</p>"#;
623 run(input, output);
624}
625
626#[test]
627fn src_line_1146() {
628 let input = r#"#5 bolt
629
630#hashtag"#;
631 let output = r#"<p>#5 bolt</p>
632<p>#hashtag</p>"#;
633 run(input, output);
634}
635
636#[test]
637fn src_line_1158() {
638 let input = r#"\## foo"#;
639 let output = r#"<p>## foo</p>"#;
640 run(input, output);
641}
642
643#[test]
644fn src_line_1167() {
645 let input = r#"# foo *bar* \*baz\*"#;
646 let output = r#"<h1>foo <em>bar</em> *baz*</h1>"#;
647 run(input, output);
648}
649
650#[test]
651fn src_line_1176() {
652 let input = "# foo \x20";
653 let output = r#"<h1>foo</h1>"#;
654 run(input, output);
655}
656
657#[test]
658fn src_line_1185() {
659 let input = r#" ### foo
660 ## foo
661 # foo"#;
662 let output = r#"<h3>foo</h3>
663<h2>foo</h2>
664<h1>foo</h1>"#;
665 run(input, output);
666}
667
668#[test]
669fn src_line_1198() {
670 let input = r#" # foo"#;
671 let output = r#"<pre><code># foo
672</code></pre>"#;
673 run(input, output);
674}
675
676#[test]
677fn src_line_1206() {
678 let input = r#"foo
679 # bar"#;
680 let output = r#"<p>foo
681# bar</p>"#;
682 run(input, output);
683}
684
685#[test]
686fn src_line_1217() {
687 let input = r#"## foo ##
688 ### bar ###"#;
689 let output = r#"<h2>foo</h2>
690<h3>bar</h3>"#;
691 run(input, output);
692}
693
694#[test]
695fn src_line_1228() {
696 let input = r#"# foo ##################################
697##### foo ##"#;
698 let output = r#"<h1>foo</h1>
699<h5>foo</h5>"#;
700 run(input, output);
701}
702
703#[test]
704fn src_line_1239() {
705 let input = "### foo ### \x20";
706 let output = r#"<h3>foo</h3>"#;
707 run(input, output);
708}
709
710#[test]
711fn src_line_1250() {
712 let input = r#"### foo ### b"#;
713 let output = r#"<h3>foo ### b</h3>"#;
714 run(input, output);
715}
716
717#[test]
718fn src_line_1259() {
719 let input = r#"# foo#"#;
720 let output = r#"<h1>foo#</h1>"#;
721 run(input, output);
722}
723
724#[test]
725fn src_line_1269() {
726 let input = r#"### foo \###
727## foo #\##
728# foo \#"#;
729 let output = r#"<h3>foo ###</h3>
730<h2>foo ###</h2>
731<h1>foo #</h1>"#;
732 run(input, output);
733}
734
735#[test]
736fn src_line_1283() {
737 let input = r#"****
738## foo
739****"#;
740 let output = r#"<hr />
741<h2>foo</h2>
742<hr />"#;
743 run(input, output);
744}
745
746#[test]
747fn src_line_1294() {
748 let input = r#"Foo bar
749# baz
750Bar foo"#;
751 let output = r#"<p>Foo bar</p>
752<h1>baz</h1>
753<p>Bar foo</p>"#;
754 run(input, output);
755}
756
757#[test]
758fn src_line_1307() {
759 let input = "##\x20
760#
761### ###";
762 let output = r#"<h2></h2>
763<h1></h1>
764<h3></h3>"#;
765 run(input, output);
766}
767
768#[test]
769fn src_line_1350() {
770 let input = r#"Foo *bar*
771=========
772
773Foo *bar*
774---------"#;
775 let output = r#"<h1>Foo <em>bar</em></h1>
776<h2>Foo <em>bar</em></h2>"#;
777 run(input, output);
778}
779
780#[test]
781fn src_line_1364() {
782 let input = r#"Foo *bar
783baz*
784===="#;
785 let output = r#"<h1>Foo <em>bar
786baz</em></h1>"#;
787 run(input, output);
788}
789
790#[test]
791fn src_line_1378() {
792 let input = " Foo *bar
793baz*\t
794====";
795 let output = r#"<h1>Foo <em>bar
796baz</em></h1>"#;
797 run(input, output);
798}
799
800#[test]
801fn src_line_1390() {
802 let input = r#"Foo
803-------------------------
804
805Foo
806="#;
807 let output = r#"<h2>Foo</h2>
808<h1>Foo</h1>"#;
809 run(input, output);
810}
811
812#[test]
813fn src_line_1405() {
814 let input = r#" Foo
815---
816
817 Foo
818-----
819
820 Foo
821 ==="#;
822 let output = r#"<h2>Foo</h2>
823<h2>Foo</h2>
824<h1>Foo</h1>"#;
825 run(input, output);
826}
827
828#[test]
829fn src_line_1423() {
830 let input = r#" Foo
831 ---
832
833 Foo
834---"#;
835 let output = r#"<pre><code>Foo
836---
837
838Foo
839</code></pre>
840<hr />"#;
841 run(input, output);
842}
843
844#[test]
845fn src_line_1442() {
846 let input = "Foo
847 ---- \x20";
848 let output = r#"<h2>Foo</h2>"#;
849 run(input, output);
850}
851
852#[test]
853fn src_line_1452() {
854 let input = r#"Foo
855 ---"#;
856 let output = r#"<p>Foo
857---</p>"#;
858 run(input, output);
859}
860
861#[test]
862fn src_line_1463() {
863 let input = r#"Foo
864= =
865
866Foo
867--- -"#;
868 let output = r#"<p>Foo
869= =</p>
870<p>Foo</p>
871<hr />"#;
872 run(input, output);
873}
874
875#[test]
876fn src_line_1479() {
877 let input = "Foo \x20
878-----";
879 let output = r#"<h2>Foo</h2>"#;
880 run(input, output);
881}
882
883#[test]
884fn src_line_1489() {
885 let input = r#"Foo\
886----"#;
887 let output = r#"<h2>Foo\</h2>"#;
888 run(input, output);
889}
890
891#[test]
892fn src_line_1500() {
893 let input = r#"`Foo
894----
895`
896
897<a title="a lot
898---
899of dashes"/>"#;
900 let output = r#"<h2>`Foo</h2>
901<p>`</p>
902<h2>&lt;a title=&quot;a lot</h2>
903<p>of dashes&quot;/&gt;</p>"#;
904 run(input, output);
905}
906
907#[test]
908fn src_line_1519() {
909 let input = r#"> Foo
910---"#;
911 let output = r#"<blockquote>
912<p>Foo</p>
913</blockquote>
914<hr />"#;
915 run(input, output);
916}
917
918#[test]
919fn src_line_1530() {
920 let input = r#"> foo
921bar
922==="#;
923 let output = r#"<blockquote>
924<p>foo
925bar
926===</p>
927</blockquote>"#;
928 run(input, output);
929}
930
931#[test]
932fn src_line_1543() {
933 let input = r#"- Foo
934---"#;
935 let output = r#"<ul>
936<li>Foo</li>
937</ul>
938<hr />"#;
939 run(input, output);
940}
941
942#[test]
943fn src_line_1558() {
944 let input = r#"Foo
945Bar
946---"#;
947 let output = r#"<h2>Foo
948Bar</h2>"#;
949 run(input, output);
950}
951
952#[test]
953fn src_line_1571() {
954 let input = r#"---
955Foo
956---
957Bar
958---
959Baz"#;
960 let output = r#"<hr />
961<h2>Foo</h2>
962<h2>Bar</h2>
963<p>Baz</p>"#;
964 run(input, output);
965}
966
967#[test]
968fn src_line_1588() {
969 let input = r#"
970===="#;
971 let output = r#"<p>====</p>"#;
972 run(input, output);
973}
974
975#[test]
976fn src_line_1600() {
977 let input = r#"---
978---"#;
979 let output = r#"<hr />
980<hr />"#;
981 run(input, output);
982}
983
984#[test]
985fn src_line_1609() {
986 let input = r#"- foo
987-----"#;
988 let output = r#"<ul>
989<li>foo</li>
990</ul>
991<hr />"#;
992 run(input, output);
993}
994
995#[test]
996fn src_line_1620() {
997 let input = r#" foo
998---"#;
999 let output = r#"<pre><code>foo
1000</code></pre>
1001<hr />"#;
1002 run(input, output);
1003}
1004
1005#[test]
1006fn src_line_1630() {
1007 let input = r#"> foo
1008-----"#;
1009 let output = r#"<blockquote>
1010<p>foo</p>
1011</blockquote>
1012<hr />"#;
1013 run(input, output);
1014}
1015
1016#[test]
1017fn src_line_1644() {
1018 let input = r#"\> foo
1019------"#;
1020 let output = r#"<h2>&gt; foo</h2>"#;
1021 run(input, output);
1022}
1023
1024#[test]
1025fn src_line_1675() {
1026 let input = r#"Foo
1027
1028bar
1029---
1030baz"#;
1031 let output = r#"<p>Foo</p>
1032<h2>bar</h2>
1033<p>baz</p>"#;
1034 run(input, output);
1035}
1036
1037#[test]
1038fn src_line_1691() {
1039 let input = r#"Foo
1040bar
1041
1042---
1043
1044baz"#;
1045 let output = r#"<p>Foo
1046bar</p>
1047<hr />
1048<p>baz</p>"#;
1049 run(input, output);
1050}
1051
1052#[test]
1053fn src_line_1709() {
1054 let input = r#"Foo
1055bar
1056* * *
1057baz"#;
1058 let output = r#"<p>Foo
1059bar</p>
1060<hr />
1061<p>baz</p>"#;
1062 run(input, output);
1063}
1064
1065#[test]
1066fn src_line_1724() {
1067 let input = r#"Foo
1068bar
1069\---
1070baz"#;
1071 let output = r#"<p>Foo
1072bar
1073---
1074baz</p>"#;
1075 run(input, output);
1076}
1077
1078#[test]
1079fn src_line_1752() {
1080 let input = r#" a simple
1081 indented code block"#;
1082 let output = r#"<pre><code>a simple
1083 indented code block
1084</code></pre>"#;
1085 run(input, output);
1086}
1087
1088#[test]
1089fn src_line_1766() {
1090 let input = r#" - foo
1091
1092 bar"#;
1093 let output = r#"<ul>
1094<li>
1095<p>foo</p>
1096<p>bar</p>
1097</li>
1098</ul>"#;
1099 run(input, output);
1100}
1101
1102#[test]
1103fn src_line_1780() {
1104 let input = r#"1. foo
1105
1106 - bar"#;
1107 let output = r#"<ol>
1108<li>
1109<p>foo</p>
1110<ul>
1111<li>bar</li>
1112</ul>
1113</li>
1114</ol>"#;
1115 run(input, output);
1116}
1117
1118#[test]
1119fn src_line_1800() {
1120 let input = r#" <a/>
1121 *hi*
1122
1123 - one"#;
1124 let output = r#"<pre><code>&lt;a/&gt;
1125*hi*
1126
1127- one
1128</code></pre>"#;
1129 run(input, output);
1130}
1131
1132#[test]
1133fn src_line_1816() {
1134 let input = " chunk1
1135
1136 chunk2
1137 \x20
1138\x20
1139\x20
1140 chunk3";
1141 let output = r#"<pre><code>chunk1
1142
1143chunk2
1144
1145
1146
1147chunk3
1148</code></pre>"#;
1149 run(input, output);
1150}
1151
1152#[test]
1153fn src_line_1839() {
1154 let input = " chunk1
1155 \x20
1156 chunk2";
1157 let output = "<pre><code>chunk1
1158 \x20
1159 chunk2
1160</code></pre>";
1161 run(input, output);
1162}
1163
1164#[test]
1165fn src_line_1854() {
1166 let input = r#"Foo
1167 bar
1168"#;
1169 let output = r#"<p>Foo
1170bar</p>"#;
1171 run(input, output);
1172}
1173
1174#[test]
1175fn src_line_1868() {
1176 let input = r#" foo
1177bar"#;
1178 let output = r#"<pre><code>foo
1179</code></pre>
1180<p>bar</p>"#;
1181 run(input, output);
1182}
1183
1184#[test]
1185fn src_line_1881() {
1186 let input = r#"# Heading
1187 foo
1188Heading
1189------
1190 foo
1191----"#;
1192 let output = r#"<h1>Heading</h1>
1193<pre><code>foo
1194</code></pre>
1195<h2>Heading</h2>
1196<pre><code>foo
1197</code></pre>
1198<hr />"#;
1199 run(input, output);
1200}
1201
1202#[test]
1203fn src_line_1901() {
1204 let input = r#" foo
1205 bar"#;
1206 let output = r#"<pre><code> foo
1207bar
1208</code></pre>"#;
1209 run(input, output);
1210}
1211
1212#[test]
1213fn src_line_1914() {
1214 let input = "
1215 \x20
1216 foo
1217 \x20
1218";
1219 let output = r#"<pre><code>foo
1220</code></pre>"#;
1221 run(input, output);
1222}
1223
1224#[test]
1225fn src_line_1928() {
1226 let input = " foo \x20";
1227 let output = "<pre><code>foo \x20
1228</code></pre>";
1229 run(input, output);
1230}
1231
1232#[test]
1233fn src_line_1983() {
1234 let input = r#"```
1235<
1236 >
1237```"#;
1238 let output = r#"<pre><code>&lt;
1239 &gt;
1240</code></pre>"#;
1241 run(input, output);
1242}
1243
1244#[test]
1245fn src_line_1997() {
1246 let input = r#"~~~
1247<
1248 >
1249~~~"#;
1250 let output = r#"<pre><code>&lt;
1251 &gt;
1252</code></pre>"#;
1253 run(input, output);
1254}
1255
1256#[test]
1257fn src_line_2010() {
1258 let input = r#"``
1259foo
1260``"#;
1261 let output = r#"<p><code>foo</code></p>"#;
1262 run(input, output);
1263}
1264
1265#[test]
1266fn src_line_2021() {
1267 let input = r#"```
1268aaa
1269~~~
1270```"#;
1271 let output = r#"<pre><code>aaa
1272~~~
1273</code></pre>"#;
1274 run(input, output);
1275}
1276
1277#[test]
1278fn src_line_2033() {
1279 let input = r#"~~~
1280aaa
1281```
1282~~~"#;
1283 let output = r#"<pre><code>aaa
1284```
1285</code></pre>"#;
1286 run(input, output);
1287}
1288
1289#[test]
1290fn src_line_2047() {
1291 let input = r#"````
1292aaa
1293```
1294``````"#;
1295 let output = r#"<pre><code>aaa
1296```
1297</code></pre>"#;
1298 run(input, output);
1299}
1300
1301#[test]
1302fn src_line_2059() {
1303 let input = r#"~~~~
1304aaa
1305~~~
1306~~~~"#;
1307 let output = r#"<pre><code>aaa
1308~~~
1309</code></pre>"#;
1310 run(input, output);
1311}
1312
1313#[test]
1314fn src_line_2074() {
1315 let input = r#"```"#;
1316 let output = r#"<pre><code></code></pre>"#;
1317 run(input, output);
1318}
1319
1320#[test]
1321fn src_line_2081() {
1322 let input = r#"`````
1323
1324```
1325aaa"#;
1326 let output = r#"<pre><code>
1327```
1328aaa
1329</code></pre>"#;
1330 run(input, output);
1331}
1332
1333#[test]
1334fn src_line_2094() {
1335 let input = r#"> ```
1336> aaa
1337
1338bbb"#;
1339 let output = r#"<blockquote>
1340<pre><code>aaa
1341</code></pre>
1342</blockquote>
1343<p>bbb</p>"#;
1344 run(input, output);
1345}
1346
1347#[test]
1348fn src_line_2110() {
1349 let input = "```
1350
1351 \x20
1352```";
1353 let output = "<pre><code>
1354 \x20
1355</code></pre>";
1356 run(input, output);
1357}
1358
1359#[test]
1360fn src_line_2124() {
1361 let input = r#"```
1362```"#;
1363 let output = r#"<pre><code></code></pre>"#;
1364 run(input, output);
1365}
1366
1367#[test]
1368fn src_line_2136() {
1369 let input = r#" ```
1370 aaa
1371aaa
1372```"#;
1373 let output = r#"<pre><code>aaa
1374aaa
1375</code></pre>"#;
1376 run(input, output);
1377}
1378
1379#[test]
1380fn src_line_2148() {
1381 let input = r#" ```
1382aaa
1383 aaa
1384aaa
1385 ```"#;
1386 let output = r#"<pre><code>aaa
1387aaa
1388aaa
1389</code></pre>"#;
1390 run(input, output);
1391}
1392
1393#[test]
1394fn src_line_2162() {
1395 let input = r#" ```
1396 aaa
1397 aaa
1398 aaa
1399 ```"#;
1400 let output = r#"<pre><code>aaa
1401 aaa
1402aaa
1403</code></pre>"#;
1404 run(input, output);
1405}
1406
1407#[test]
1408fn src_line_2178() {
1409 let input = r#" ```
1410 aaa
1411 ```"#;
1412 let output = r#"<pre><code>```
1413aaa
1414```
1415</code></pre>"#;
1416 run(input, output);
1417}
1418
1419#[test]
1420fn src_line_2193() {
1421 let input = r#"```
1422aaa
1423 ```"#;
1424 let output = r#"<pre><code>aaa
1425</code></pre>"#;
1426 run(input, output);
1427}
1428
1429#[test]
1430fn src_line_2203() {
1431 let input = r#" ```
1432aaa
1433 ```"#;
1434 let output = r#"<pre><code>aaa
1435</code></pre>"#;
1436 run(input, output);
1437}
1438
1439#[test]
1440fn src_line_2215() {
1441 let input = r#"```
1442aaa
1443 ```"#;
1444 let output = r#"<pre><code>aaa
1445 ```
1446</code></pre>"#;
1447 run(input, output);
1448}
1449
1450#[test]
1451fn src_line_2229() {
1452 let input = r#"``` ```
1453aaa"#;
1454 let output = r#"<p><code> </code>
1455aaa</p>"#;
1456 run(input, output);
1457}
1458
1459#[test]
1460fn src_line_2238() {
1461 let input = r#"~~~~~~
1462aaa
1463~~~ ~~"#;
1464 let output = r#"<pre><code>aaa
1465~~~ ~~
1466</code></pre>"#;
1467 run(input, output);
1468}
1469
1470#[test]
1471fn src_line_2252() {
1472 let input = r#"foo
1473```
1474bar
1475```
1476baz"#;
1477 let output = r#"<p>foo</p>
1478<pre><code>bar
1479</code></pre>
1480<p>baz</p>"#;
1481 run(input, output);
1482}
1483
1484#[test]
1485fn src_line_2269() {
1486 let input = r#"foo
1487---
1488~~~
1489bar
1490~~~
1491# baz"#;
1492 let output = r#"<h2>foo</h2>
1493<pre><code>bar
1494</code></pre>
1495<h1>baz</h1>"#;
1496 run(input, output);
1497}
1498
1499#[test]
1500fn src_line_2291() {
1501 let input = r#"```ruby
1502def foo(x)
1503 return 3
1504end
1505```"#;
1506 let output = r#"<pre><code class="language-ruby">def foo(x)
1507 return 3
1508end
1509</code></pre>"#;
1510 run(input, output);
1511}
1512
1513#[test]
1514fn src_line_2305() {
1515 let input = r#"~~~~ ruby startline=3 $%@#$
1516def foo(x)
1517 return 3
1518end
1519~~~~~~~"#;
1520 let output = r#"<pre><code class="language-ruby">def foo(x)
1521 return 3
1522end
1523</code></pre>"#;
1524 run(input, output);
1525}
1526
1527#[test]
1528fn src_line_2319() {
1529 let input = r#"````;
1530````"#;
1531 let output = r#"<pre><code class="language-;"></code></pre>"#;
1532 run(input, output);
1533}
1534
1535#[test]
1536fn src_line_2329() {
1537 let input = r#"``` aa ```
1538foo"#;
1539 let output = r#"<p><code>aa</code>
1540foo</p>"#;
1541 run(input, output);
1542}
1543
1544#[test]
1545fn src_line_2340() {
1546 let input = r#"~~~ aa ``` ~~~
1547foo
1548~~~"#;
1549 let output = r#"<pre><code class="language-aa">foo
1550</code></pre>"#;
1551 run(input, output);
1552}
1553
1554#[test]
1555fn src_line_2352() {
1556 let input = r#"```
1557``` aaa
1558```"#;
1559 let output = r#"<pre><code>``` aaa
1560</code></pre>"#;
1561 run(input, output);
1562}
1563
1564#[test]
1565fn src_line_2431() {
1566 let input = r#"<table><tr><td>
1567<pre>
1568**Hello**,
1569
1570_world_.
1571</pre>
1572</td></tr></table>"#;
1573 let output = r#"<table><tr><td>
1574<pre>
1575**Hello**,
1576<p><em>world</em>.
1577</pre></p>
1578</td></tr></table>"#;
1579 run(input, output);
1580}
1581
1582#[test]
1583fn src_line_2460() {
1584 let input = r#"<table>
1585 <tr>
1586 <td>
1587 hi
1588 </td>
1589 </tr>
1590</table>
1591
1592okay."#;
1593 let output = r#"<table>
1594 <tr>
1595 <td>
1596 hi
1597 </td>
1598 </tr>
1599</table>
1600<p>okay.</p>"#;
1601 run(input, output);
1602}
1603
1604#[test]
1605fn src_line_2482() {
1606 let input = r#" <div>
1607 *hello*
1608 <foo><a>"#;
1609 let output = r#" <div>
1610 *hello*
1611 <foo><a>"#;
1612 run(input, output);
1613}
1614
1615#[test]
1616fn src_line_2495() {
1617 let input = r#"</div>
1618*foo*"#;
1619 let output = r#"</div>
1620*foo*"#;
1621 run(input, output);
1622}
1623
1624#[test]
1625fn src_line_2506() {
1626 let input = r#"<DIV CLASS="foo">
1627
1628*Markdown*
1629
1630</DIV>"#;
1631 let output = r#"<DIV CLASS="foo">
1632<p><em>Markdown</em></p>
1633</DIV>"#;
1634 run(input, output);
1635}
1636
1637#[test]
1638fn src_line_2522() {
1639 let input = r#"<div id="foo"
1640 class="bar">
1641</div>"#;
1642 let output = r#"<div id="foo"
1643 class="bar">
1644</div>"#;
1645 run(input, output);
1646}
1647
1648#[test]
1649fn src_line_2533() {
1650 let input = r#"<div id="foo" class="bar
1651 baz">
1652</div>"#;
1653 let output = r#"<div id="foo" class="bar
1654 baz">
1655</div>"#;
1656 run(input, output);
1657}
1658
1659#[test]
1660fn src_line_2545() {
1661 let input = r#"<div>
1662*foo*
1663
1664*bar*"#;
1665 let output = r#"<div>
1666*foo*
1667<p><em>bar</em></p>"#;
1668 run(input, output);
1669}
1670
1671#[test]
1672fn src_line_2561() {
1673 let input = r#"<div id="foo"
1674*hi*"#;
1675 let output = r#"<div id="foo"
1676*hi*"#;
1677 run(input, output);
1678}
1679
1680#[test]
1681fn src_line_2570() {
1682 let input = r#"<div class
1683foo"#;
1684 let output = r#"<div class
1685foo"#;
1686 run(input, output);
1687}
1688
1689#[test]
1690fn src_line_2582() {
1691 let input = r#"<div *???-&&&-<---
1692*foo*"#;
1693 let output = r#"<div *???-&&&-<---
1694*foo*"#;
1695 run(input, output);
1696}
1697
1698#[test]
1699fn src_line_2594() {
1700 let input = r#"<div><a href="bar">*foo*</a></div>"#;
1701 let output = r#"<div><a href="bar">*foo*</a></div>"#;
1702 run(input, output);
1703}
1704
1705#[test]
1706fn src_line_2601() {
1707 let input = r#"<table><tr><td>
1708foo
1709</td></tr></table>"#;
1710 let output = r#"<table><tr><td>
1711foo
1712</td></tr></table>"#;
1713 run(input, output);
1714}
1715
1716#[test]
1717fn src_line_2618() {
1718 let input = r#"<div></div>
1719``` c
1720int x = 33;
1721```"#;
1722 let output = r#"<div></div>
1723``` c
1724int x = 33;
1725```"#;
1726 run(input, output);
1727}
1728
1729#[test]
1730fn src_line_2635() {
1731 let input = r#"<a href="foo">
1732*bar*
1733</a>"#;
1734 let output = r#"<a href="foo">
1735*bar*
1736</a>"#;
1737 run(input, output);
1738}
1739
1740#[test]
1741fn src_line_2648() {
1742 let input = r#"<Warning>
1743*bar*
1744</Warning>"#;
1745 let output = r#"<Warning>
1746*bar*
1747</Warning>"#;
1748 run(input, output);
1749}
1750
1751#[test]
1752fn src_line_2659() {
1753 let input = r#"<i class="foo">
1754*bar*
1755</i>"#;
1756 let output = r#"<i class="foo">
1757*bar*
1758</i>"#;
1759 run(input, output);
1760}
1761
1762#[test]
1763fn src_line_2670() {
1764 let input = r#"</ins>
1765*bar*"#;
1766 let output = r#"</ins>
1767*bar*"#;
1768 run(input, output);
1769}
1770
1771#[test]
1772fn src_line_2685() {
1773 let input = r#"<del>
1774*foo*
1775</del>"#;
1776 let output = r#"<del>
1777*foo*
1778</del>"#;
1779 run(input, output);
1780}
1781
1782#[test]
1783fn src_line_2700() {
1784 let input = r#"<del>
1785
1786*foo*
1787
1788</del>"#;
1789 let output = r#"<del>
1790<p><em>foo</em></p>
1791</del>"#;
1792 run(input, output);
1793}
1794
1795#[test]
1796fn src_line_2718() {
1797 let input = r#"<del>*foo*</del>"#;
1798 let output = r#"<p><del><em>foo</em></del></p>"#;
1799 run(input, output);
1800}
1801
1802#[test]
1803fn src_line_2734() {
1804 let input = r#"<pre language="haskell"><code>
1805import Text.HTML.TagSoup
1806
1807main :: IO ()
1808main = print $ parseTags tags
1809</code></pre>
1810okay"#;
1811 let output = r#"<pre language="haskell"><code>
1812import Text.HTML.TagSoup
1813
1814main :: IO ()
1815main = print $ parseTags tags
1816</code></pre>
1817<p>okay</p>"#;
1818 run(input, output);
1819}
1820
1821#[test]
1822fn src_line_2755() {
1823 let input = r#"<script type="text/javascript">
1824// JavaScript example
1825
1826document.getElementById("demo").innerHTML = "Hello JavaScript!";
1827</script>
1828okay"#;
1829 let output = r#"<script type="text/javascript">
1830// JavaScript example
1831
1832document.getElementById("demo").innerHTML = "Hello JavaScript!";
1833</script>
1834<p>okay</p>"#;
1835 run(input, output);
1836}
1837
1838#[test]
1839fn src_line_2774() {
1840 let input = r#"<textarea>
1841
1842*foo*
1843
1844_bar_
1845
1846</textarea>"#;
1847 let output = r#"<textarea>
1848
1849*foo*
1850
1851_bar_
1852
1853</textarea>"#;
1854 run(input, output);
1855}
1856
1857#[test]
1858fn src_line_2794() {
1859 let input = r#"<style
1860 type="text/css">
1861h1 {color:red;}
1862
1863p {color:blue;}
1864</style>
1865okay"#;
1866 let output = r#"<style
1867 type="text/css">
1868h1 {color:red;}
1869
1870p {color:blue;}
1871</style>
1872<p>okay</p>"#;
1873 run(input, output);
1874}
1875
1876#[test]
1877fn src_line_2817() {
1878 let input = r#"<style
1879 type="text/css">
1880
1881foo"#;
1882 let output = r#"<style
1883 type="text/css">
1884
1885foo"#;
1886 run(input, output);
1887}
1888
1889#[test]
1890fn src_line_2830() {
1891 let input = r#"> <div>
1892> foo
1893
1894bar"#;
1895 let output = r#"<blockquote>
1896<div>
1897foo
1898</blockquote>
1899<p>bar</p>"#;
1900 run(input, output);
1901}
1902
1903#[test]
1904fn src_line_2844() {
1905 let input = r#"- <div>
1906- foo"#;
1907 let output = r#"<ul>
1908<li>
1909<div>
1910</li>
1911<li>foo</li>
1912</ul>"#;
1913 run(input, output);
1914}
1915
1916#[test]
1917fn src_line_2859() {
1918 let input = r#"<style>p{color:red;}</style>
1919*foo*"#;
1920 let output = r#"<style>p{color:red;}</style>
1921<p><em>foo</em></p>"#;
1922 run(input, output);
1923}
1924
1925#[test]
1926fn src_line_2868() {
1927 let input = r#"<!-- foo -->*bar*
1928*baz*"#;
1929 let output = r#"<!-- foo -->*bar*
1930<p><em>baz</em></p>"#;
1931 run(input, output);
1932}
1933
1934#[test]
1935fn src_line_2880() {
1936 let input = r#"<script>
1937foo
1938</script>1. *bar*"#;
1939 let output = r#"<script>
1940foo
1941</script>1. *bar*"#;
1942 run(input, output);
1943}
1944
1945#[test]
1946fn src_line_2893() {
1947 let input = r#"<!-- Foo
1948
1949bar
1950 baz -->
1951okay"#;
1952 let output = r#"<!-- Foo
1953
1954bar
1955 baz -->
1956<p>okay</p>"#;
1957 run(input, output);
1958}
1959
1960#[test]
1961fn src_line_2911() {
1962 let input = r#"<?php
1963
1964 echo '>';
1965
1966?>
1967okay"#;
1968 let output = r#"<?php
1969
1970 echo '>';
1971
1972?>
1973<p>okay</p>"#;
1974 run(input, output);
1975}
1976
1977#[test]
1978fn src_line_2930() {
1979 let input = r#"<!DOCTYPE html>"#;
1980 let output = r#"<!DOCTYPE html>"#;
1981 run(input, output);
1982}
1983
1984#[test]
1985fn src_line_2939() {
1986 let input = r#"<![CDATA[
1987function matchwo(a,b)
1988{
1989 if (a < b && a < 0) then {
1990 return 1;
1991
1992 } else {
1993
1994 return 0;
1995 }
1996}
1997]]>
1998okay"#;
1999 let output = r#"<![CDATA[
2000function matchwo(a,b)
2001{
2002 if (a < b && a < 0) then {
2003 return 1;
2004
2005 } else {
2006
2007 return 0;
2008 }
2009}
2010]]>
2011<p>okay</p>"#;
2012 run(input, output);
2013}
2014
2015#[test]
2016fn src_line_2973() {
2017 let input = r#" <!-- foo -->
2018
2019 <!-- foo -->"#;
2020 let output = r#" <!-- foo -->
2021<pre><code>&lt;!-- foo --&gt;
2022</code></pre>"#;
2023 run(input, output);
2024}
2025
2026#[test]
2027fn src_line_2984() {
2028 let input = r#" <div>
2029
2030 <div>"#;
2031 let output = r#" <div>
2032<pre><code>&lt;div&gt;
2033</code></pre>"#;
2034 run(input, output);
2035}
2036
2037#[test]
2038fn src_line_2998() {
2039 let input = r#"Foo
2040<div>
2041bar
2042</div>"#;
2043 let output = r#"<p>Foo</p>
2044<div>
2045bar
2046</div>"#;
2047 run(input, output);
2048}
2049
2050#[test]
2051fn src_line_3015() {
2052 let input = r#"<div>
2053bar
2054</div>
2055*foo*"#;
2056 let output = r#"<div>
2057bar
2058</div>
2059*foo*"#;
2060 run(input, output);
2061}
2062
2063#[test]
2064fn src_line_3030() {
2065 let input = r#"Foo
2066<a href="bar">
2067baz"#;
2068 let output = r#"<p>Foo
2069<a href="bar">
2070baz</p>"#;
2071 run(input, output);
2072}
2073
2074#[test]
2075fn src_line_3071() {
2076 let input = r#"<div>
2077
2078*Emphasized* text.
2079
2080</div>"#;
2081 let output = r#"<div>
2082<p><em>Emphasized</em> text.</p>
2083</div>"#;
2084 run(input, output);
2085}
2086
2087#[test]
2088fn src_line_3084() {
2089 let input = r#"<div>
2090*Emphasized* text.
2091</div>"#;
2092 let output = r#"<div>
2093*Emphasized* text.
2094</div>"#;
2095 run(input, output);
2096}
2097
2098#[test]
2099fn src_line_3106() {
2100 let input = r#"<table>
2101
2102<tr>
2103
2104<td>
2105Hi
2106</td>
2107
2108</tr>
2109
2110</table>"#;
2111 let output = r#"<table>
2112<tr>
2113<td>
2114Hi
2115</td>
2116</tr>
2117</table>"#;
2118 run(input, output);
2119}
2120
2121#[test]
2122fn src_line_3133() {
2123 let input = r#"<table>
2124
2125 <tr>
2126
2127 <td>
2128 Hi
2129 </td>
2130
2131 </tr>
2132
2133</table>"#;
2134 let output = r#"<table>
2135 <tr>
2136<pre><code>&lt;td&gt;
2137 Hi
2138&lt;/td&gt;
2139</code></pre>
2140 </tr>
2141</table>"#;
2142 run(input, output);
2143}
2144
2145#[test]
2146fn src_line_3182() {
2147 let input = r#"[foo]: /url "title"
2148
2149[foo]"#;
2150 let output = r#"<p><a href="/url" title="title">foo</a></p>"#;
2151 run(input, output);
2152}
2153
2154#[test]
2155fn src_line_3191() {
2156 let input = " [foo]:\x20
2157 /url \x20
2158 'the title' \x20
2159
2160[foo]";
2161 let output = r#"<p><a href="/url" title="the title">foo</a></p>"#;
2162 run(input, output);
2163}
2164
2165#[test]
2166fn src_line_3202() {
2167 let input = r#"[Foo*bar\]]:my_(url) 'title (with parens)'
2168
2169[Foo*bar\]]"#;
2170 let output = r#"<p><a href="my_(url)" title="title (with parens)">Foo*bar]</a></p>"#;
2171 run(input, output);
2172}
2173
2174#[test]
2175fn src_line_3211() {
2176 let input = r#"[Foo bar]:
2177<my url>
2178'title'
2179
2180[Foo bar]"#;
2181 let output = r#"<p><a href="my%20url" title="title">Foo bar</a></p>"#;
2182 run(input, output);
2183}
2184
2185#[test]
2186fn src_line_3224() {
2187 let input = r#"[foo]: /url '
2188title
2189line1
2190line2
2191'
2192
2193[foo]"#;
2194 let output = r#"<p><a href="/url" title="
2195title
2196line1
2197line2
2198">foo</a></p>"#;
2199 run(input, output);
2200}
2201
2202#[test]
2203fn src_line_3243() {
2204 let input = r#"[foo]: /url 'title
2205
2206with blank line'
2207
2208[foo]"#;
2209 let output = r#"<p>[foo]: /url 'title</p>
2210<p>with blank line'</p>
2211<p>[foo]</p>"#;
2212 run(input, output);
2213}
2214
2215#[test]
2216fn src_line_3258() {
2217 let input = r#"[foo]:
2218/url
2219
2220[foo]"#;
2221 let output = r#"<p><a href="/url">foo</a></p>"#;
2222 run(input, output);
2223}
2224
2225#[test]
2226fn src_line_3270() {
2227 let input = r#"[foo]:
2228
2229[foo]"#;
2230 let output = r#"<p>[foo]:</p>
2231<p>[foo]</p>"#;
2232 run(input, output);
2233}
2234
2235#[test]
2236fn src_line_3282() {
2237 let input = r#"[foo]: <>
2238
2239[foo]"#;
2240 let output = r#"<p><a href="">foo</a></p>"#;
2241 run(input, output);
2242}
2243
2244#[test]
2245fn src_line_3293() {
2246 let input = r#"[foo]: <bar>(baz)
2247
2248[foo]"#;
2249 let output = r#"<p>[foo]: <bar>(baz)</p>
2250<p>[foo]</p>"#;
2251 run(input, output);
2252}
2253
2254#[test]
2255fn src_line_3306() {
2256 let input = r#"[foo]: /url\bar\*baz "foo\"bar\baz"
2257
2258[foo]"#;
2259 let output = r#"<p><a href="/url%5Cbar*baz" title="foo&quot;bar\baz">foo</a></p>"#;
2260 run(input, output);
2261}
2262
2263#[test]
2264fn src_line_3317() {
2265 let input = r#"[foo]
2266
2267[foo]: url"#;
2268 let output = r#"<p><a href="url">foo</a></p>"#;
2269 run(input, output);
2270}
2271
2272#[test]
2273fn src_line_3329() {
2274 let input = r#"[foo]
2275
2276[foo]: first
2277[foo]: second"#;
2278 let output = r#"<p><a href="first">foo</a></p>"#;
2279 run(input, output);
2280}
2281
2282#[test]
2283fn src_line_3342() {
2284 let input = r#"[FOO]: /url
2285
2286[Foo]"#;
2287 let output = r#"<p><a href="/url">Foo</a></p>"#;
2288 run(input, output);
2289}
2290
2291#[test]
2292fn src_line_3351() {
2293 let input = r#"[ΑΓΩ]: /φου
2294
2295[αγω]"#;
2296 let output = r#"<p><a href="/%CF%86%CE%BF%CF%85">αγω</a></p>"#;
2297 run(input, output);
2298}
2299
2300#[test]
2301fn src_line_3366() {
2302 let input = r#"[foo]: /url"#;
2303 let output = r#""#;
2304 run(input, output);
2305}
2306
2307#[test]
2308fn src_line_3374() {
2309 let input = r#"[
2310foo
2311]: /url
2312bar"#;
2313 let output = r#"<p>bar</p>"#;
2314 run(input, output);
2315}
2316
2317#[test]
2318fn src_line_3387() {
2319 let input = r#"[foo]: /url "title" ok"#;
2320 let output = r#"<p>[foo]: /url &quot;title&quot; ok</p>"#;
2321 run(input, output);
2322}
2323
2324#[test]
2325fn src_line_3396() {
2326 let input = r#"[foo]: /url
2327"title" ok"#;
2328 let output = r#"<p>&quot;title&quot; ok</p>"#;
2329 run(input, output);
2330}
2331
2332#[test]
2333fn src_line_3407() {
2334 let input = r#" [foo]: /url "title"
2335
2336[foo]"#;
2337 let output = r#"<pre><code>[foo]: /url &quot;title&quot;
2338</code></pre>
2339<p>[foo]</p>"#;
2340 run(input, output);
2341}
2342
2343#[test]
2344fn src_line_3421() {
2345 let input = r#"```
2346[foo]: /url
2347```
2348
2349[foo]"#;
2350 let output = r#"<pre><code>[foo]: /url
2351</code></pre>
2352<p>[foo]</p>"#;
2353 run(input, output);
2354}
2355
2356#[test]
2357fn src_line_3436() {
2358 let input = r#"Foo
2359[bar]: /baz
2360
2361[bar]"#;
2362 let output = r#"<p>Foo
2363[bar]: /baz</p>
2364<p>[bar]</p>"#;
2365 run(input, output);
2366}
2367
2368#[test]
2369fn src_line_3451() {
2370 let input = r#"# [Foo]
2371[foo]: /url
2372> bar"#;
2373 let output = r#"<h1><a href="/url">Foo</a></h1>
2374<blockquote>
2375<p>bar</p>
2376</blockquote>"#;
2377 run(input, output);
2378}
2379
2380#[test]
2381fn src_line_3462() {
2382 let input = r#"[foo]: /url
2383bar
2384===
2385[foo]"#;
2386 let output = r#"<h1>bar</h1>
2387<p><a href="/url">foo</a></p>"#;
2388 run(input, output);
2389}
2390
2391#[test]
2392fn src_line_3472() {
2393 let input = r#"[foo]: /url
2394===
2395[foo]"#;
2396 let output = r#"<p>===
2397<a href="/url">foo</a></p>"#;
2398 run(input, output);
2399}
2400
2401#[test]
2402fn src_line_3485() {
2403 let input = r#"[foo]: /foo-url "foo"
2404[bar]: /bar-url
2405 "bar"
2406[baz]: /baz-url
2407
2408[foo],
2409[bar],
2410[baz]"#;
2411 let output = r#"<p><a href="/foo-url" title="foo">foo</a>,
2412<a href="/bar-url" title="bar">bar</a>,
2413<a href="/baz-url">baz</a></p>"#;
2414 run(input, output);
2415}
2416
2417#[test]
2418fn src_line_3506() {
2419 let input = r#"[foo]
2420
2421> [foo]: /url"#;
2422 let output = r#"<p><a href="/url">foo</a></p>
2423<blockquote>
2424</blockquote>"#;
2425 run(input, output);
2426}
2427
2428#[test]
2429fn src_line_3528() {
2430 let input = r#"aaa
2431
2432bbb"#;
2433 let output = r#"<p>aaa</p>
2434<p>bbb</p>"#;
2435 run(input, output);
2436}
2437
2438#[test]
2439fn src_line_3540() {
2440 let input = r#"aaa
2441bbb
2442
2443ccc
2444ddd"#;
2445 let output = r#"<p>aaa
2446bbb</p>
2447<p>ccc
2448ddd</p>"#;
2449 run(input, output);
2450}
2451
2452#[test]
2453fn src_line_3556() {
2454 let input = r#"aaa
2455
2456
2457bbb"#;
2458 let output = r#"<p>aaa</p>
2459<p>bbb</p>"#;
2460 run(input, output);
2461}
2462
2463#[test]
2464fn src_line_3569() {
2465 let input = r#" aaa
2466 bbb"#;
2467 let output = r#"<p>aaa
2468bbb</p>"#;
2469 run(input, output);
2470}
2471
2472#[test]
2473fn src_line_3581() {
2474 let input = r#"aaa
2475 bbb
2476 ccc"#;
2477 let output = r#"<p>aaa
2478bbb
2479ccc</p>"#;
2480 run(input, output);
2481}
2482
2483#[test]
2484fn src_line_3595() {
2485 let input = r#" aaa
2486bbb"#;
2487 let output = r#"<p>aaa
2488bbb</p>"#;
2489 run(input, output);
2490}
2491
2492#[test]
2493fn src_line_3604() {
2494 let input = r#" aaa
2495bbb"#;
2496 let output = r#"<pre><code>aaa
2497</code></pre>
2498<p>bbb</p>"#;
2499 run(input, output);
2500}
2501
2502#[test]
2503fn src_line_3618() {
2504 let input = "aaa \x20
2505bbb \x20";
2506 let output = r#"<p>aaa<br />
2507bbb</p>"#;
2508 run(input, output);
2509}
2510
2511#[test]
2512fn src_line_3635() {
2513 let input = " \x20
2514
2515aaa
2516 \x20
2517
2518# aaa
2519
2520 \x20";
2521 let output = r#"<p>aaa</p>
2522<h1>aaa</h1>"#;
2523 run(input, output);
2524}
2525
2526#[test]
2527fn src_line_3703() {
2528 let input = r#"> # Foo
2529> bar
2530> baz"#;
2531 let output = r#"<blockquote>
2532<h1>Foo</h1>
2533<p>bar
2534baz</p>
2535</blockquote>"#;
2536 run(input, output);
2537}
2538
2539#[test]
2540fn src_line_3718() {
2541 let input = r#"># Foo
2542>bar
2543> baz"#;
2544 let output = r#"<blockquote>
2545<h1>Foo</h1>
2546<p>bar
2547baz</p>
2548</blockquote>"#;
2549 run(input, output);
2550}
2551
2552#[test]
2553fn src_line_3733() {
2554 let input = r#" > # Foo
2555 > bar
2556 > baz"#;
2557 let output = r#"<blockquote>
2558<h1>Foo</h1>
2559<p>bar
2560baz</p>
2561</blockquote>"#;
2562 run(input, output);
2563}
2564
2565#[test]
2566fn src_line_3748() {
2567 let input = r#" > # Foo
2568 > bar
2569 > baz"#;
2570 let output = r#"<pre><code>&gt; # Foo
2571&gt; bar
2572&gt; baz
2573</code></pre>"#;
2574 run(input, output);
2575}
2576
2577#[test]
2578fn src_line_3763() {
2579 let input = r#"> # Foo
2580> bar
2581baz"#;
2582 let output = r#"<blockquote>
2583<h1>Foo</h1>
2584<p>bar
2585baz</p>
2586</blockquote>"#;
2587 run(input, output);
2588}
2589
2590#[test]
2591fn src_line_3779() {
2592 let input = r#"> bar
2593baz
2594> foo"#;
2595 let output = r#"<blockquote>
2596<p>bar
2597baz
2598foo</p>
2599</blockquote>"#;
2600 run(input, output);
2601}
2602
2603#[test]
2604fn src_line_3803() {
2605 let input = r#"> foo
2606---"#;
2607 let output = r#"<blockquote>
2608<p>foo</p>
2609</blockquote>
2610<hr />"#;
2611 run(input, output);
2612}
2613
2614#[test]
2615fn src_line_3823() {
2616 let input = r#"> - foo
2617- bar"#;
2618 let output = r#"<blockquote>
2619<ul>
2620<li>foo</li>
2621</ul>
2622</blockquote>
2623<ul>
2624<li>bar</li>
2625</ul>"#;
2626 run(input, output);
2627}
2628
2629#[test]
2630fn src_line_3841() {
2631 let input = r#"> foo
2632 bar"#;
2633 let output = r#"<blockquote>
2634<pre><code>foo
2635</code></pre>
2636</blockquote>
2637<pre><code>bar
2638</code></pre>"#;
2639 run(input, output);
2640}
2641
2642#[test]
2643fn src_line_3854() {
2644 let input = r#"> ```
2645foo
2646```"#;
2647 let output = r#"<blockquote>
2648<pre><code></code></pre>
2649</blockquote>
2650<p>foo</p>
2651<pre><code></code></pre>"#;
2652 run(input, output);
2653}
2654
2655#[test]
2656fn src_line_3870() {
2657 let input = r#"> foo
2658 - bar"#;
2659 let output = r#"<blockquote>
2660<p>foo
2661- bar</p>
2662</blockquote>"#;
2663 run(input, output);
2664}
2665
2666#[test]
2667fn src_line_3894() {
2668 let input = r#">"#;
2669 let output = r#"<blockquote>
2670</blockquote>"#;
2671 run(input, output);
2672}
2673
2674#[test]
2675fn src_line_3902() {
2676 let input = ">
2677> \x20
2678>\x20";
2679 let output = r#"<blockquote>
2680</blockquote>"#;
2681 run(input, output);
2682}
2683
2684#[test]
2685fn src_line_3914() {
2686 let input = ">
2687> foo
2688> \x20";
2689 let output = r#"<blockquote>
2690<p>foo</p>
2691</blockquote>"#;
2692 run(input, output);
2693}
2694
2695#[test]
2696fn src_line_3927() {
2697 let input = r#"> foo
2698
2699> bar"#;
2700 let output = r#"<blockquote>
2701<p>foo</p>
2702</blockquote>
2703<blockquote>
2704<p>bar</p>
2705</blockquote>"#;
2706 run(input, output);
2707}
2708
2709#[test]
2710fn src_line_3949() {
2711 let input = r#"> foo
2712> bar"#;
2713 let output = r#"<blockquote>
2714<p>foo
2715bar</p>
2716</blockquote>"#;
2717 run(input, output);
2718}
2719
2720#[test]
2721fn src_line_3962() {
2722 let input = r#"> foo
2723>
2724> bar"#;
2725 let output = r#"<blockquote>
2726<p>foo</p>
2727<p>bar</p>
2728</blockquote>"#;
2729 run(input, output);
2730}
2731
2732#[test]
2733fn src_line_3976() {
2734 let input = r#"foo
2735> bar"#;
2736 let output = r#"<p>foo</p>
2737<blockquote>
2738<p>bar</p>
2739</blockquote>"#;
2740 run(input, output);
2741}
2742
2743#[test]
2744fn src_line_3990() {
2745 let input = r#"> aaa
2746***
2747> bbb"#;
2748 let output = r#"<blockquote>
2749<p>aaa</p>
2750</blockquote>
2751<hr />
2752<blockquote>
2753<p>bbb</p>
2754</blockquote>"#;
2755 run(input, output);
2756}
2757
2758#[test]
2759fn src_line_4008() {
2760 let input = r#"> bar
2761baz"#;
2762 let output = r#"<blockquote>
2763<p>bar
2764baz</p>
2765</blockquote>"#;
2766 run(input, output);
2767}
2768
2769#[test]
2770fn src_line_4019() {
2771 let input = r#"> bar
2772
2773baz"#;
2774 let output = r#"<blockquote>
2775<p>bar</p>
2776</blockquote>
2777<p>baz</p>"#;
2778 run(input, output);
2779}
2780
2781#[test]
2782fn src_line_4031() {
2783 let input = r#"> bar
2784>
2785baz"#;
2786 let output = r#"<blockquote>
2787<p>bar</p>
2788</blockquote>
2789<p>baz</p>"#;
2790 run(input, output);
2791}
2792
2793#[test]
2794fn src_line_4047() {
2795 let input = r#"> > > foo
2796bar"#;
2797 let output = r#"<blockquote>
2798<blockquote>
2799<blockquote>
2800<p>foo
2801bar</p>
2802</blockquote>
2803</blockquote>
2804</blockquote>"#;
2805 run(input, output);
2806}
2807
2808#[test]
2809fn src_line_4062() {
2810 let input = r#">>> foo
2811> bar
2812>>baz"#;
2813 let output = r#"<blockquote>
2814<blockquote>
2815<blockquote>
2816<p>foo
2817bar
2818baz</p>
2819</blockquote>
2820</blockquote>
2821</blockquote>"#;
2822 run(input, output);
2823}
2824
2825#[test]
2826fn src_line_4084() {
2827 let input = r#"> code
2828
2829> not code"#;
2830 let output = r#"<blockquote>
2831<pre><code>code
2832</code></pre>
2833</blockquote>
2834<blockquote>
2835<p>not code</p>
2836</blockquote>"#;
2837 run(input, output);
2838}
2839
2840#[test]
2841fn src_line_4138() {
2842 let input = r#"A paragraph
2843with two lines.
2844
2845 indented code
2846
2847> A block quote."#;
2848 let output = r#"<p>A paragraph
2849with two lines.</p>
2850<pre><code>indented code
2851</code></pre>
2852<blockquote>
2853<p>A block quote.</p>
2854</blockquote>"#;
2855 run(input, output);
2856}
2857
2858#[test]
2859fn src_line_4160() {
2860 let input = r#"1. A paragraph
2861 with two lines.
2862
2863 indented code
2864
2865 > A block quote."#;
2866 let output = r#"<ol>
2867<li>
2868<p>A paragraph
2869with two lines.</p>
2870<pre><code>indented code
2871</code></pre>
2872<blockquote>
2873<p>A block quote.</p>
2874</blockquote>
2875</li>
2876</ol>"#;
2877 run(input, output);
2878}
2879
2880#[test]
2881fn src_line_4193() {
2882 let input = r#"- one
2883
2884 two"#;
2885 let output = r#"<ul>
2886<li>one</li>
2887</ul>
2888<p>two</p>"#;
2889 run(input, output);
2890}
2891
2892#[test]
2893fn src_line_4205() {
2894 let input = r#"- one
2895
2896 two"#;
2897 let output = r#"<ul>
2898<li>
2899<p>one</p>
2900<p>two</p>
2901</li>
2902</ul>"#;
2903 run(input, output);
2904}
2905
2906#[test]
2907fn src_line_4219() {
2908 let input = r#" - one
2909
2910 two"#;
2911 let output = r#"<ul>
2912<li>one</li>
2913</ul>
2914<pre><code> two
2915</code></pre>"#;
2916 run(input, output);
2917}
2918
2919#[test]
2920fn src_line_4232() {
2921 let input = r#" - one
2922
2923 two"#;
2924 let output = r#"<ul>
2925<li>
2926<p>one</p>
2927<p>two</p>
2928</li>
2929</ul>"#;
2930 run(input, output);
2931}
2932
2933#[test]
2934fn src_line_4254() {
2935 let input = r#" > > 1. one
2936>>
2937>> two"#;
2938 let output = r#"<blockquote>
2939<blockquote>
2940<ol>
2941<li>
2942<p>one</p>
2943<p>two</p>
2944</li>
2945</ol>
2946</blockquote>
2947</blockquote>"#;
2948 run(input, output);
2949}
2950
2951#[test]
2952fn src_line_4281() {
2953 let input = r#">>- one
2954>>
2955 > > two"#;
2956 let output = r#"<blockquote>
2957<blockquote>
2958<ul>
2959<li>one</li>
2960</ul>
2961<p>two</p>
2962</blockquote>
2963</blockquote>"#;
2964 run(input, output);
2965}
2966
2967#[test]
2968fn src_line_4300() {
2969 let input = r#"-one
2970
29712.two"#;
2972 let output = r#"<p>-one</p>
2973<p>2.two</p>"#;
2974 run(input, output);
2975}
2976
2977#[test]
2978fn src_line_4313() {
2979 let input = r#"- foo
2980
2981
2982 bar"#;
2983 let output = r#"<ul>
2984<li>
2985<p>foo</p>
2986<p>bar</p>
2987</li>
2988</ul>"#;
2989 run(input, output);
2990}
2991
2992#[test]
2993fn src_line_4330() {
2994 let input = r#"1. foo
2995
2996 ```
2997 bar
2998 ```
2999
3000 baz
3001
3002 > bam"#;
3003 let output = r#"<ol>
3004<li>
3005<p>foo</p>
3006<pre><code>bar
3007</code></pre>
3008<p>baz</p>
3009<blockquote>
3010<p>bam</p>
3011</blockquote>
3012</li>
3013</ol>"#;
3014 run(input, output);
3015}
3016
3017#[test]
3018fn src_line_4358() {
3019 let input = r#"- Foo
3020
3021 bar
3022
3023
3024 baz"#;
3025 let output = r#"<ul>
3026<li>
3027<p>Foo</p>
3028<pre><code>bar
3029
3030
3031baz
3032</code></pre>
3033</li>
3034</ul>"#;
3035 run(input, output);
3036}
3037
3038#[test]
3039fn src_line_4380() {
3040 let input = r#"123456789. ok"#;
3041 let output = r#"<ol start="123456789">
3042<li>ok</li>
3043</ol>"#;
3044 run(input, output);
3045}
3046
3047#[test]
3048fn src_line_4389() {
3049 let input = r#"1234567890. not ok"#;
3050 let output = r#"<p>1234567890. not ok</p>"#;
3051 run(input, output);
3052}
3053
3054#[test]
3055fn src_line_4398() {
3056 let input = r#"0. ok"#;
3057 let output = r#"<ol start="0">
3058<li>ok</li>
3059</ol>"#;
3060 run(input, output);
3061}
3062
3063#[test]
3064fn src_line_4407() {
3065 let input = r#"003. ok"#;
3066 let output = r#"<ol start="3">
3067<li>ok</li>
3068</ol>"#;
3069 run(input, output);
3070}
3071
3072#[test]
3073fn src_line_4418() {
3074 let input = r#"-1. not ok"#;
3075 let output = r#"<p>-1. not ok</p>"#;
3076 run(input, output);
3077}
3078
3079#[test]
3080fn src_line_4441() {
3081 let input = r#"- foo
3082
3083 bar"#;
3084 let output = r#"<ul>
3085<li>
3086<p>foo</p>
3087<pre><code>bar
3088</code></pre>
3089</li>
3090</ul>"#;
3091 run(input, output);
3092}
3093
3094#[test]
3095fn src_line_4458() {
3096 let input = r#" 10. foo
3097
3098 bar"#;
3099 let output = r#"<ol start="10">
3100<li>
3101<p>foo</p>
3102<pre><code>bar
3103</code></pre>
3104</li>
3105</ol>"#;
3106 run(input, output);
3107}
3108
3109#[test]
3110fn src_line_4477() {
3111 let input = r#" indented code
3112
3113paragraph
3114
3115 more code"#;
3116 let output = r#"<pre><code>indented code
3117</code></pre>
3118<p>paragraph</p>
3119<pre><code>more code
3120</code></pre>"#;
3121 run(input, output);
3122}
3123
3124#[test]
3125fn src_line_4492() {
3126 let input = r#"1. indented code
3127
3128 paragraph
3129
3130 more code"#;
3131 let output = r#"<ol>
3132<li>
3133<pre><code>indented code
3134</code></pre>
3135<p>paragraph</p>
3136<pre><code>more code
3137</code></pre>
3138</li>
3139</ol>"#;
3140 run(input, output);
3141}
3142
3143#[test]
3144fn src_line_4514() {
3145 let input = r#"1. indented code
3146
3147 paragraph
3148
3149 more code"#;
3150 let output = r#"<ol>
3151<li>
3152<pre><code> indented code
3153</code></pre>
3154<p>paragraph</p>
3155<pre><code>more code
3156</code></pre>
3157</li>
3158</ol>"#;
3159 run(input, output);
3160}
3161
3162#[test]
3163fn src_line_4541() {
3164 let input = r#" foo
3165
3166bar"#;
3167 let output = r#"<p>foo</p>
3168<p>bar</p>"#;
3169 run(input, output);
3170}
3171
3172#[test]
3173fn src_line_4551() {
3174 let input = r#"- foo
3175
3176 bar"#;
3177 let output = r#"<ul>
3178<li>foo</li>
3179</ul>
3180<p>bar</p>"#;
3181 run(input, output);
3182}
3183
3184#[test]
3185fn src_line_4568() {
3186 let input = r#"- foo
3187
3188 bar"#;
3189 let output = r#"<ul>
3190<li>
3191<p>foo</p>
3192<p>bar</p>
3193</li>
3194</ul>"#;
3195 run(input, output);
3196}
3197
3198#[test]
3199fn src_line_4595() {
3200 let input = r#"-
3201 foo
3202-
3203 ```
3204 bar
3205 ```
3206-
3207 baz"#;
3208 let output = r#"<ul>
3209<li>foo</li>
3210<li>
3211<pre><code>bar
3212</code></pre>
3213</li>
3214<li>
3215<pre><code>baz
3216</code></pre>
3217</li>
3218</ul>"#;
3219 run(input, output);
3220}
3221
3222#[test]
3223fn src_line_4621() {
3224 let input = "- \x20
3225 foo";
3226 let output = r#"<ul>
3227<li>foo</li>
3228</ul>"#;
3229 run(input, output);
3230}
3231
3232#[test]
3233fn src_line_4635() {
3234 let input = r#"-
3235
3236 foo"#;
3237 let output = r#"<ul>
3238<li></li>
3239</ul>
3240<p>foo</p>"#;
3241 run(input, output);
3242}
3243
3244#[test]
3245fn src_line_4649() {
3246 let input = r#"- foo
3247-
3248- bar"#;
3249 let output = r#"<ul>
3250<li>foo</li>
3251<li></li>
3252<li>bar</li>
3253</ul>"#;
3254 run(input, output);
3255}
3256
3257#[test]
3258fn src_line_4664() {
3259 let input = "- foo
3260- \x20
3261- bar";
3262 let output = r#"<ul>
3263<li>foo</li>
3264<li></li>
3265<li>bar</li>
3266</ul>"#;
3267 run(input, output);
3268}
3269
3270#[test]
3271fn src_line_4679() {
3272 let input = r#"1. foo
32732.
32743. bar"#;
3275 let output = r#"<ol>
3276<li>foo</li>
3277<li></li>
3278<li>bar</li>
3279</ol>"#;
3280 run(input, output);
3281}
3282
3283#[test]
3284fn src_line_4694() {
3285 let input = r#"*"#;
3286 let output = r#"<ul>
3287<li></li>
3288</ul>"#;
3289 run(input, output);
3290}
3291
3292#[test]
3293fn src_line_4704() {
3294 let input = r#"foo
3295*
3296
3297foo
32981."#;
3299 let output = r#"<p>foo
3300*</p>
3301<p>foo
33021.</p>"#;
3303 run(input, output);
3304}
3305
3306#[test]
3307fn src_line_4726() {
3308 let input = r#" 1. A paragraph
3309 with two lines.
3310
3311 indented code
3312
3313 > A block quote."#;
3314 let output = r#"<ol>
3315<li>
3316<p>A paragraph
3317with two lines.</p>
3318<pre><code>indented code
3319</code></pre>
3320<blockquote>
3321<p>A block quote.</p>
3322</blockquote>
3323</li>
3324</ol>"#;
3325 run(input, output);
3326}
3327
3328#[test]
3329fn src_line_4750() {
3330 let input = r#" 1. A paragraph
3331 with two lines.
3332
3333 indented code
3334
3335 > A block quote."#;
3336 let output = r#"<ol>
3337<li>
3338<p>A paragraph
3339with two lines.</p>
3340<pre><code>indented code
3341</code></pre>
3342<blockquote>
3343<p>A block quote.</p>
3344</blockquote>
3345</li>
3346</ol>"#;
3347 run(input, output);
3348}
3349
3350#[test]
3351fn src_line_4774() {
3352 let input = r#" 1. A paragraph
3353 with two lines.
3354
3355 indented code
3356
3357 > A block quote."#;
3358 let output = r#"<ol>
3359<li>
3360<p>A paragraph
3361with two lines.</p>
3362<pre><code>indented code
3363</code></pre>
3364<blockquote>
3365<p>A block quote.</p>
3366</blockquote>
3367</li>
3368</ol>"#;
3369 run(input, output);
3370}
3371
3372#[test]
3373fn src_line_4798() {
3374 let input = r#" 1. A paragraph
3375 with two lines.
3376
3377 indented code
3378
3379 > A block quote."#;
3380 let output = r#"<pre><code>1. A paragraph
3381 with two lines.
3382
3383 indented code
3384
3385 &gt; A block quote.
3386</code></pre>"#;
3387 run(input, output);
3388}
3389
3390#[test]
3391fn src_line_4828() {
3392 let input = r#" 1. A paragraph
3393with two lines.
3394
3395 indented code
3396
3397 > A block quote."#;
3398 let output = r#"<ol>
3399<li>
3400<p>A paragraph
3401with two lines.</p>
3402<pre><code>indented code
3403</code></pre>
3404<blockquote>
3405<p>A block quote.</p>
3406</blockquote>
3407</li>
3408</ol>"#;
3409 run(input, output);
3410}
3411
3412#[test]
3413fn src_line_4852() {
3414 let input = r#" 1. A paragraph
3415 with two lines."#;
3416 let output = r#"<ol>
3417<li>A paragraph
3418with two lines.</li>
3419</ol>"#;
3420 run(input, output);
3421}
3422
3423#[test]
3424fn src_line_4865() {
3425 let input = r#"> 1. > Blockquote
3426continued here."#;
3427 let output = r#"<blockquote>
3428<ol>
3429<li>
3430<blockquote>
3431<p>Blockquote
3432continued here.</p>
3433</blockquote>
3434</li>
3435</ol>
3436</blockquote>"#;
3437 run(input, output);
3438}
3439
3440#[test]
3441fn src_line_4882() {
3442 let input = r#"> 1. > Blockquote
3443> continued here."#;
3444 let output = r#"<blockquote>
3445<ol>
3446<li>
3447<blockquote>
3448<p>Blockquote
3449continued here.</p>
3450</blockquote>
3451</li>
3452</ol>
3453</blockquote>"#;
3454 run(input, output);
3455}
3456
3457#[test]
3458fn src_line_4910() {
3459 let input = r#"- foo
3460 - bar
3461 - baz
3462 - boo"#;
3463 let output = r#"<ul>
3464<li>foo
3465<ul>
3466<li>bar
3467<ul>
3468<li>baz
3469<ul>
3470<li>boo</li>
3471</ul>
3472</li>
3473</ul>
3474</li>
3475</ul>
3476</li>
3477</ul>"#;
3478 run(input, output);
3479}
3480
3481#[test]
3482fn src_line_4936() {
3483 let input = r#"- foo
3484 - bar
3485 - baz
3486 - boo"#;
3487 let output = r#"<ul>
3488<li>foo</li>
3489<li>bar</li>
3490<li>baz</li>
3491<li>boo</li>
3492</ul>"#;
3493 run(input, output);
3494}
3495
3496#[test]
3497fn src_line_4953() {
3498 let input = r#"10) foo
3499 - bar"#;
3500 let output = r#"<ol start="10">
3501<li>foo
3502<ul>
3503<li>bar</li>
3504</ul>
3505</li>
3506</ol>"#;
3507 run(input, output);
3508}
3509
3510#[test]
3511fn src_line_4969() {
3512 let input = r#"10) foo
3513 - bar"#;
3514 let output = r#"<ol start="10">
3515<li>foo</li>
3516</ol>
3517<ul>
3518<li>bar</li>
3519</ul>"#;
3520 run(input, output);
3521}
3522
3523#[test]
3524fn src_line_4984() {
3525 let input = r#"- - foo"#;
3526 let output = r#"<ul>
3527<li>
3528<ul>
3529<li>foo</li>
3530</ul>
3531</li>
3532</ul>"#;
3533 run(input, output);
3534}
3535
3536#[test]
3537fn src_line_4997() {
3538 let input = r#"1. - 2. foo"#;
3539 let output = r#"<ol>
3540<li>
3541<ul>
3542<li>
3543<ol start="2">
3544<li>foo</li>
3545</ol>
3546</li>
3547</ul>
3548</li>
3549</ol>"#;
3550 run(input, output);
3551}
3552
3553#[test]
3554fn src_line_5016() {
3555 let input = r#"- # Foo
3556- Bar
3557 ---
3558 baz"#;
3559 let output = r#"<ul>
3560<li>
3561<h1>Foo</h1>
3562</li>
3563<li>
3564<h2>Bar</h2>
3565baz</li>
3566</ul>"#;
3567 run(input, output);
3568}
3569
3570#[test]
3571fn src_line_5252() {
3572 let input = r#"- foo
3573- bar
3574+ baz"#;
3575 let output = r#"<ul>
3576<li>foo</li>
3577<li>bar</li>
3578</ul>
3579<ul>
3580<li>baz</li>
3581</ul>"#;
3582 run(input, output);
3583}
3584
3585#[test]
3586fn src_line_5267() {
3587 let input = r#"1. foo
35882. bar
35893) baz"#;
3590 let output = r#"<ol>
3591<li>foo</li>
3592<li>bar</li>
3593</ol>
3594<ol start="3">
3595<li>baz</li>
3596</ol>"#;
3597 run(input, output);
3598}
3599
3600#[test]
3601fn src_line_5286() {
3602 let input = r#"Foo
3603- bar
3604- baz"#;
3605 let output = r#"<p>Foo</p>
3606<ul>
3607<li>bar</li>
3608<li>baz</li>
3609</ul>"#;
3610 run(input, output);
3611}
3612
3613#[test]
3614fn src_line_5363() {
3615 let input = r#"The number of windows in my house is
361614. The number of doors is 6."#;
3617 let output = r#"<p>The number of windows in my house is
361814. The number of doors is 6.</p>"#;
3619 run(input, output);
3620}
3621
3622#[test]
3623fn src_line_5373() {
3624 let input = r#"The number of windows in my house is
36251. The number of doors is 6."#;
3626 let output = r#"<p>The number of windows in my house is</p>
3627<ol>
3628<li>The number of doors is 6.</li>
3629</ol>"#;
3630 run(input, output);
3631}
3632
3633#[test]
3634fn src_line_5387() {
3635 let input = r#"- foo
3636
3637- bar
3638
3639
3640- baz"#;
3641 let output = r#"<ul>
3642<li>
3643<p>foo</p>
3644</li>
3645<li>
3646<p>bar</p>
3647</li>
3648<li>
3649<p>baz</p>
3650</li>
3651</ul>"#;
3652 run(input, output);
3653}
3654
3655#[test]
3656fn src_line_5408() {
3657 let input = r#"- foo
3658 - bar
3659 - baz
3660
3661
3662 bim"#;
3663 let output = r#"<ul>
3664<li>foo
3665<ul>
3666<li>bar
3667<ul>
3668<li>
3669<p>baz</p>
3670<p>bim</p>
3671</li>
3672</ul>
3673</li>
3674</ul>
3675</li>
3676</ul>"#;
3677 run(input, output);
3678}
3679
3680#[test]
3681fn src_line_5438() {
3682 let input = r#"- foo
3683- bar
3684
3685<!-- -->
3686
3687- baz
3688- bim"#;
3689 let output = r#"<ul>
3690<li>foo</li>
3691<li>bar</li>
3692</ul>
3693<!-- -->
3694<ul>
3695<li>baz</li>
3696<li>bim</li>
3697</ul>"#;
3698 run(input, output);
3699}
3700
3701#[test]
3702fn src_line_5459() {
3703 let input = r#"- foo
3704
3705 notcode
3706
3707- foo
3708
3709<!-- -->
3710
3711 code"#;
3712 let output = r#"<ul>
3713<li>
3714<p>foo</p>
3715<p>notcode</p>
3716</li>
3717<li>
3718<p>foo</p>
3719</li>
3720</ul>
3721<!-- -->
3722<pre><code>code
3723</code></pre>"#;
3724 run(input, output);
3725}
3726
3727#[test]
3728fn src_line_5490() {
3729 let input = r#"- a
3730 - b
3731 - c
3732 - d
3733 - e
3734 - f
3735- g"#;
3736 let output = r#"<ul>
3737<li>a</li>
3738<li>b</li>
3739<li>c</li>
3740<li>d</li>
3741<li>e</li>
3742<li>f</li>
3743<li>g</li>
3744</ul>"#;
3745 run(input, output);
3746}
3747
3748#[test]
3749fn src_line_5511() {
3750 let input = r#"1. a
3751
3752 2. b
3753
3754 3. c"#;
3755 let output = r#"<ol>
3756<li>
3757<p>a</p>
3758</li>
3759<li>
3760<p>b</p>
3761</li>
3762<li>
3763<p>c</p>
3764</li>
3765</ol>"#;
3766 run(input, output);
3767}
3768
3769#[test]
3770fn src_line_5535() {
3771 let input = r#"- a
3772 - b
3773 - c
3774 - d
3775 - e"#;
3776 let output = r#"<ul>
3777<li>a</li>
3778<li>b</li>
3779<li>c</li>
3780<li>d
3781- e</li>
3782</ul>"#;
3783 run(input, output);
3784}
3785
3786#[test]
3787fn src_line_5555() {
3788 let input = r#"1. a
3789
3790 2. b
3791
3792 3. c"#;
3793 let output = r#"<ol>
3794<li>
3795<p>a</p>
3796</li>
3797<li>
3798<p>b</p>
3799</li>
3800</ol>
3801<pre><code>3. c
3802</code></pre>"#;
3803 run(input, output);
3804}
3805
3806#[test]
3807fn src_line_5578() {
3808 let input = r#"- a
3809- b
3810
3811- c"#;
3812 let output = r#"<ul>
3813<li>
3814<p>a</p>
3815</li>
3816<li>
3817<p>b</p>
3818</li>
3819<li>
3820<p>c</p>
3821</li>
3822</ul>"#;
3823 run(input, output);
3824}
3825
3826#[test]
3827fn src_line_5600() {
3828 let input = r#"* a
3829*
3830
3831* c"#;
3832 let output = r#"<ul>
3833<li>
3834<p>a</p>
3835</li>
3836<li></li>
3837<li>
3838<p>c</p>
3839</li>
3840</ul>"#;
3841 run(input, output);
3842}
3843
3844#[test]
3845fn src_line_5622() {
3846 let input = r#"- a
3847- b
3848
3849 c
3850- d"#;
3851 let output = r#"<ul>
3852<li>
3853<p>a</p>
3854</li>
3855<li>
3856<p>b</p>
3857<p>c</p>
3858</li>
3859<li>
3860<p>d</p>
3861</li>
3862</ul>"#;
3863 run(input, output);
3864}
3865
3866#[test]
3867fn src_line_5644() {
3868 let input = r#"- a
3869- b
3870
3871 [ref]: /url
3872- d"#;
3873 let output = r#"<ul>
3874<li>
3875<p>a</p>
3876</li>
3877<li>
3878<p>b</p>
3879</li>
3880<li>
3881<p>d</p>
3882</li>
3883</ul>"#;
3884 run(input, output);
3885}
3886
3887#[test]
3888fn src_line_5667() {
3889 let input = r#"- a
3890- ```
3891 b
3892
3893
3894 ```
3895- c"#;
3896 let output = r#"<ul>
3897<li>a</li>
3898<li>
3899<pre><code>b
3900
3901
3902</code></pre>
3903</li>
3904<li>c</li>
3905</ul>"#;
3906 run(input, output);
3907}
3908
3909#[test]
3910fn src_line_5693() {
3911 let input = r#"- a
3912 - b
3913
3914 c
3915- d"#;
3916 let output = r#"<ul>
3917<li>a
3918<ul>
3919<li>
3920<p>b</p>
3921<p>c</p>
3922</li>
3923</ul>
3924</li>
3925<li>d</li>
3926</ul>"#;
3927 run(input, output);
3928}
3929
3930#[test]
3931fn src_line_5717() {
3932 let input = r#"* a
3933 > b
3934 >
3935* c"#;
3936 let output = r#"<ul>
3937<li>a
3938<blockquote>
3939<p>b</p>
3940</blockquote>
3941</li>
3942<li>c</li>
3943</ul>"#;
3944 run(input, output);
3945}
3946
3947#[test]
3948fn src_line_5737() {
3949 let input = r#"- a
3950 > b
3951 ```
3952 c
3953 ```
3954- d"#;
3955 let output = r#"<ul>
3956<li>a
3957<blockquote>
3958<p>b</p>
3959</blockquote>
3960<pre><code>c
3961</code></pre>
3962</li>
3963<li>d</li>
3964</ul>"#;
3965 run(input, output);
3966}
3967
3968#[test]
3969fn src_line_5760() {
3970 let input = r#"- a"#;
3971 let output = r#"<ul>
3972<li>a</li>
3973</ul>"#;
3974 run(input, output);
3975}
3976
3977#[test]
3978fn src_line_5769() {
3979 let input = r#"- a
3980 - b"#;
3981 let output = r#"<ul>
3982<li>a
3983<ul>
3984<li>b</li>
3985</ul>
3986</li>
3987</ul>"#;
3988 run(input, output);
3989}
3990
3991#[test]
3992fn src_line_5786() {
3993 let input = r#"1. ```
3994 foo
3995 ```
3996
3997 bar"#;
3998 let output = r#"<ol>
3999<li>
4000<pre><code>foo
4001</code></pre>
4002<p>bar</p>
4003</li>
4004</ol>"#;
4005 run(input, output);
4006}
4007
4008#[test]
4009fn src_line_5805() {
4010 let input = r#"* foo
4011 * bar
4012
4013 baz"#;
4014 let output = r#"<ul>
4015<li>
4016<p>foo</p>
4017<ul>
4018<li>bar</li>
4019</ul>
4020<p>baz</p>
4021</li>
4022</ul>"#;
4023 run(input, output);
4024}
4025
4026#[test]
4027fn src_line_5823() {
4028 let input = r#"- a
4029 - b
4030 - c
4031
4032- d
4033 - e
4034 - f"#;
4035 let output = r#"<ul>
4036<li>
4037<p>a</p>
4038<ul>
4039<li>b</li>
4040<li>c</li>
4041</ul>
4042</li>
4043<li>
4044<p>d</p>
4045<ul>
4046<li>e</li>
4047<li>f</li>
4048</ul>
4049</li>
4050</ul>"#;
4051 run(input, output);
4052}
4053
4054#[test]
4055fn src_line_5857() {
4056 let input = r#"`hi`lo`"#;
4057 let output = r#"<p><code>hi</code>lo`</p>"#;
4058 run(input, output);
4059}
4060
4061#[test]
4062fn src_line_5889() {
4063 let input = r#"`foo`"#;
4064 let output = r#"<p><code>foo</code></p>"#;
4065 run(input, output);
4066}
4067
4068#[test]
4069fn src_line_5900() {
4070 let input = r#"`` foo ` bar ``"#;
4071 let output = r#"<p><code>foo ` bar</code></p>"#;
4072 run(input, output);
4073}
4074
4075#[test]
4076fn src_line_5910() {
4077 let input = r#"` `` `"#;
4078 let output = r#"<p><code>``</code></p>"#;
4079 run(input, output);
4080}
4081
4082#[test]
4083fn src_line_5918() {
4084 let input = r#"` `` `"#;
4085 let output = r#"<p><code> `` </code></p>"#;
4086 run(input, output);
4087}
4088
4089#[test]
4090fn src_line_5927() {
4091 let input = r#"` a`"#;
4092 let output = r#"<p><code> a</code></p>"#;
4093 run(input, output);
4094}
4095
4096#[test]
4097fn src_line_5936() {
4098 let input = r#"` b `"#;
4099 let output = r#"<p><code> b </code></p>"#;
4100 run(input, output);
4101}
4102
4103#[test]
4104fn src_line_5944() {
4105 let input = r#"` `
4106` `"#;
4107 let output = r#"<p><code> </code>
4108<code> </code></p>"#;
4109 run(input, output);
4110}
4111
4112#[test]
4113fn src_line_5955() {
4114 let input = "``
4115foo
4116bar \x20
4117baz
4118``";
4119 let output = r#"<p><code>foo bar baz</code></p>"#;
4120 run(input, output);
4121}
4122
4123#[test]
4124fn src_line_5965() {
4125 let input = "``
4126foo\x20
4127``";
4128 let output = r#"<p><code>foo </code></p>"#;
4129 run(input, output);
4130}
4131
4132#[test]
4133fn src_line_5976() {
4134 let input = "`foo bar\x20
4135baz`";
4136 let output = r#"<p><code>foo bar baz</code></p>"#;
4137 run(input, output);
4138}
4139
4140#[test]
4141fn src_line_5993() {
4142 let input = r#"`foo\`bar`"#;
4143 let output = r#"<p><code>foo\</code>bar`</p>"#;
4144 run(input, output);
4145}
4146
4147#[test]
4148fn src_line_6004() {
4149 let input = r#"``foo`bar``"#;
4150 let output = r#"<p><code>foo`bar</code></p>"#;
4151 run(input, output);
4152}
4153
4154#[test]
4155fn src_line_6010() {
4156 let input = r#"` foo `` bar `"#;
4157 let output = r#"<p><code>foo `` bar</code></p>"#;
4158 run(input, output);
4159}
4160
4161#[test]
4162fn src_line_6022() {
4163 let input = r#"*foo`*`"#;
4164 let output = r#"<p>*foo<code>*</code></p>"#;
4165 run(input, output);
4166}
4167
4168#[test]
4169fn src_line_6031() {
4170 let input = r#"[not a `link](/foo`)"#;
4171 let output = r#"<p>[not a <code>link](/foo</code>)</p>"#;
4172 run(input, output);
4173}
4174
4175#[test]
4176fn src_line_6041() {
4177 let input = r#"`<a href="`">`"#;
4178 let output = r#"<p><code>&lt;a href=&quot;</code>&quot;&gt;`</p>"#;
4179 run(input, output);
4180}
4181
4182#[test]
4183fn src_line_6050() {
4184 let input = r#"<a href="`">`"#;
4185 let output = r#"<p><a href="`">`</p>"#;
4186 run(input, output);
4187}
4188
4189#[test]
4190fn src_line_6059() {
4191 let input = r#"`<http://foo.bar.`baz>`"#;
4192 let output = r#"<p><code>&lt;http://foo.bar.</code>baz&gt;`</p>"#;
4193 run(input, output);
4194}
4195
4196#[test]
4197fn src_line_6068() {
4198 let input = r#"<http://foo.bar.`baz>`"#;
4199 let output = r#"<p><a href="http://foo.bar.%60baz">http://foo.bar.`baz</a>`</p>"#;
4200 run(input, output);
4201}
4202
4203#[test]
4204fn src_line_6078() {
4205 let input = r#"```foo``"#;
4206 let output = r#"<p>```foo``</p>"#;
4207 run(input, output);
4208}
4209
4210#[test]
4211fn src_line_6085() {
4212 let input = r#"`foo"#;
4213 let output = r#"<p>`foo</p>"#;
4214 run(input, output);
4215}
4216
4217#[test]
4218fn src_line_6094() {
4219 let input = r#"`foo``bar``"#;
4220 let output = r#"<p>`foo<code>bar</code></p>"#;
4221 run(input, output);
4222}
4223
4224#[test]
4225fn src_line_6311() {
4226 let input = r#"*foo bar*"#;
4227 let output = r#"<p><em>foo bar</em></p>"#;
4228 run(input, output);
4229}
4230
4231#[test]
4232fn src_line_6321() {
4233 let input = r#"a * foo bar*"#;
4234 let output = r#"<p>a * foo bar*</p>"#;
4235 run(input, output);
4236}
4237
4238#[test]
4239fn src_line_6332() {
4240 let input = r#"a*"foo"*"#;
4241 let output = r#"<p>a*&quot;foo&quot;*</p>"#;
4242 run(input, output);
4243}
4244
4245#[test]
4246fn src_line_6341() {
4247 let input = r#"* a *"#;
4248 let output = r#"<p>* a *</p>"#;
4249 run(input, output);
4250}
4251
4252#[test]
4253fn src_line_6350() {
4254 let input = r#"foo*bar*"#;
4255 let output = r#"<p>foo<em>bar</em></p>"#;
4256 run(input, output);
4257}
4258
4259#[test]
4260fn src_line_6357() {
4261 let input = r#"5*6*78"#;
4262 let output = r#"<p>5<em>6</em>78</p>"#;
4263 run(input, output);
4264}
4265
4266#[test]
4267fn src_line_6366() {
4268 let input = r#"_foo bar_"#;
4269 let output = r#"<p><em>foo bar</em></p>"#;
4270 run(input, output);
4271}
4272
4273#[test]
4274fn src_line_6376() {
4275 let input = r#"_ foo bar_"#;
4276 let output = r#"<p>_ foo bar_</p>"#;
4277 run(input, output);
4278}
4279
4280#[test]
4281fn src_line_6386() {
4282 let input = r#"a_"foo"_"#;
4283 let output = r#"<p>a_&quot;foo&quot;_</p>"#;
4284 run(input, output);
4285}
4286
4287#[test]
4288fn src_line_6395() {
4289 let input = r#"foo_bar_"#;
4290 let output = r#"<p>foo_bar_</p>"#;
4291 run(input, output);
4292}
4293
4294#[test]
4295fn src_line_6402() {
4296 let input = r#"5_6_78"#;
4297 let output = r#"<p>5_6_78</p>"#;
4298 run(input, output);
4299}
4300
4301#[test]
4302fn src_line_6409() {
4303 let input = r#"пристаням_стремятся_"#;
4304 let output = r#"<p>пристаням_стремятся_</p>"#;
4305 run(input, output);
4306}
4307
4308#[test]
4309fn src_line_6419() {
4310 let input = r#"aa_"bb"_cc"#;
4311 let output = r#"<p>aa_&quot;bb&quot;_cc</p>"#;
4312 run(input, output);
4313}
4314
4315#[test]
4316fn src_line_6430() {
4317 let input = r#"foo-_(bar)_"#;
4318 let output = r#"<p>foo-<em>(bar)</em></p>"#;
4319 run(input, output);
4320}
4321
4322#[test]
4323fn src_line_6442() {
4324 let input = r#"_foo*"#;
4325 let output = r#"<p>_foo*</p>"#;
4326 run(input, output);
4327}
4328
4329#[test]
4330fn src_line_6452() {
4331 let input = r#"*foo bar *"#;
4332 let output = r#"<p>*foo bar *</p>"#;
4333 run(input, output);
4334}
4335
4336#[test]
4337fn src_line_6461() {
4338 let input = r#"*foo bar
4339*"#;
4340 let output = r#"<p>*foo bar
4341*</p>"#;
4342 run(input, output);
4343}
4344
4345#[test]
4346fn src_line_6474() {
4347 let input = r#"*(*foo)"#;
4348 let output = r#"<p>*(*foo)</p>"#;
4349 run(input, output);
4350}
4351
4352#[test]
4353fn src_line_6484() {
4354 let input = r#"*(*foo*)*"#;
4355 let output = r#"<p><em>(<em>foo</em>)</em></p>"#;
4356 run(input, output);
4357}
4358
4359#[test]
4360fn src_line_6493() {
4361 let input = r#"*foo*bar"#;
4362 let output = r#"<p><em>foo</em>bar</p>"#;
4363 run(input, output);
4364}
4365
4366#[test]
4367fn src_line_6506() {
4368 let input = r#"_foo bar _"#;
4369 let output = r#"<p>_foo bar _</p>"#;
4370 run(input, output);
4371}
4372
4373#[test]
4374fn src_line_6516() {
4375 let input = r#"_(_foo)"#;
4376 let output = r#"<p>_(_foo)</p>"#;
4377 run(input, output);
4378}
4379
4380#[test]
4381fn src_line_6525() {
4382 let input = r#"_(_foo_)_"#;
4383 let output = r#"<p><em>(<em>foo</em>)</em></p>"#;
4384 run(input, output);
4385}
4386
4387#[test]
4388fn src_line_6534() {
4389 let input = r#"_foo_bar"#;
4390 let output = r#"<p>_foo_bar</p>"#;
4391 run(input, output);
4392}
4393
4394#[test]
4395fn src_line_6541() {
4396 let input = r#"_пристаням_стремятся"#;
4397 let output = r#"<p>_пристаням_стремятся</p>"#;
4398 run(input, output);
4399}
4400
4401#[test]
4402fn src_line_6548() {
4403 let input = r#"_foo_bar_baz_"#;
4404 let output = r#"<p><em>foo_bar_baz</em></p>"#;
4405 run(input, output);
4406}
4407
4408#[test]
4409fn src_line_6559() {
4410 let input = r#"_(bar)_."#;
4411 let output = r#"<p><em>(bar)</em>.</p>"#;
4412 run(input, output);
4413}
4414
4415#[test]
4416fn src_line_6568() {
4417 let input = r#"**foo bar**"#;
4418 let output = r#"<p><strong>foo bar</strong></p>"#;
4419 run(input, output);
4420}
4421
4422#[test]
4423fn src_line_6578() {
4424 let input = r#"** foo bar**"#;
4425 let output = r#"<p>** foo bar**</p>"#;
4426 run(input, output);
4427}
4428
4429#[test]
4430fn src_line_6589() {
4431 let input = r#"a**"foo"**"#;
4432 let output = r#"<p>a**&quot;foo&quot;**</p>"#;
4433 run(input, output);
4434}
4435
4436#[test]
4437fn src_line_6598() {
4438 let input = r#"foo**bar**"#;
4439 let output = r#"<p>foo<strong>bar</strong></p>"#;
4440 run(input, output);
4441}
4442
4443#[test]
4444fn src_line_6607() {
4445 let input = r#"__foo bar__"#;
4446 let output = r#"<p><strong>foo bar</strong></p>"#;
4447 run(input, output);
4448}
4449
4450#[test]
4451fn src_line_6617() {
4452 let input = r#"__ foo bar__"#;
4453 let output = r#"<p>__ foo bar__</p>"#;
4454 run(input, output);
4455}
4456
4457#[test]
4458fn src_line_6625() {
4459 let input = r#"__
4460foo bar__"#;
4461 let output = r#"<p>__
4462foo bar__</p>"#;
4463 run(input, output);
4464}
4465
4466#[test]
4467fn src_line_6637() {
4468 let input = r#"a__"foo"__"#;
4469 let output = r#"<p>a__&quot;foo&quot;__</p>"#;
4470 run(input, output);
4471}
4472
4473#[test]
4474fn src_line_6646() {
4475 let input = r#"foo__bar__"#;
4476 let output = r#"<p>foo__bar__</p>"#;
4477 run(input, output);
4478}
4479
4480#[test]
4481fn src_line_6653() {
4482 let input = r#"5__6__78"#;
4483 let output = r#"<p>5__6__78</p>"#;
4484 run(input, output);
4485}
4486
4487#[test]
4488fn src_line_6660() {
4489 let input = r#"пристаням__стремятся__"#;
4490 let output = r#"<p>пристаням__стремятся__</p>"#;
4491 run(input, output);
4492}
4493
4494#[test]
4495fn src_line_6667() {
4496 let input = r#"__foo, __bar__, baz__"#;
4497 let output = r#"<p><strong>foo, <strong>bar</strong>, baz</strong></p>"#;
4498 run(input, output);
4499}
4500
4501#[test]
4502fn src_line_6678() {
4503 let input = r#"foo-__(bar)__"#;
4504 let output = r#"<p>foo-<strong>(bar)</strong></p>"#;
4505 run(input, output);
4506}
4507
4508#[test]
4509fn src_line_6691() {
4510 let input = r#"**foo bar **"#;
4511 let output = r#"<p>**foo bar **</p>"#;
4512 run(input, output);
4513}
4514
4515#[test]
4516fn src_line_6704() {
4517 let input = r#"**(**foo)"#;
4518 let output = r#"<p>**(**foo)</p>"#;
4519 run(input, output);
4520}
4521
4522#[test]
4523fn src_line_6714() {
4524 let input = r#"*(**foo**)*"#;
4525 let output = r#"<p><em>(<strong>foo</strong>)</em></p>"#;
4526 run(input, output);
4527}
4528
4529#[test]
4530fn src_line_6721() {
4531 let input = r#"**Gomphocarpus (*Gomphocarpus physocarpus*, syn.
4532*Asclepias physocarpa*)**"#;
4533 let output = r#"<p><strong>Gomphocarpus (<em>Gomphocarpus physocarpus</em>, syn.
4534<em>Asclepias physocarpa</em>)</strong></p>"#;
4535 run(input, output);
4536}
4537
4538#[test]
4539fn src_line_6730() {
4540 let input = r#"**foo "*bar*" foo**"#;
4541 let output = r#"<p><strong>foo &quot;<em>bar</em>&quot; foo</strong></p>"#;
4542 run(input, output);
4543}
4544
4545#[test]
4546fn src_line_6739() {
4547 let input = r#"**foo**bar"#;
4548 let output = r#"<p><strong>foo</strong>bar</p>"#;
4549 run(input, output);
4550}
4551
4552#[test]
4553fn src_line_6751() {
4554 let input = r#"__foo bar __"#;
4555 let output = r#"<p>__foo bar __</p>"#;
4556 run(input, output);
4557}
4558
4559#[test]
4560fn src_line_6761() {
4561 let input = r#"__(__foo)"#;
4562 let output = r#"<p>__(__foo)</p>"#;
4563 run(input, output);
4564}
4565
4566#[test]
4567fn src_line_6771() {
4568 let input = r#"_(__foo__)_"#;
4569 let output = r#"<p><em>(<strong>foo</strong>)</em></p>"#;
4570 run(input, output);
4571}
4572
4573#[test]
4574fn src_line_6780() {
4575 let input = r#"__foo__bar"#;
4576 let output = r#"<p>__foo__bar</p>"#;
4577 run(input, output);
4578}
4579
4580#[test]
4581fn src_line_6787() {
4582 let input = r#"__пристаням__стремятся"#;
4583 let output = r#"<p>__пристаням__стремятся</p>"#;
4584 run(input, output);
4585}
4586
4587#[test]
4588fn src_line_6794() {
4589 let input = r#"__foo__bar__baz__"#;
4590 let output = r#"<p><strong>foo__bar__baz</strong></p>"#;
4591 run(input, output);
4592}
4593
4594#[test]
4595fn src_line_6805() {
4596 let input = r#"__(bar)__."#;
4597 let output = r#"<p><strong>(bar)</strong>.</p>"#;
4598 run(input, output);
4599}
4600
4601#[test]
4602fn src_line_6817() {
4603 let input = r#"*foo [bar](/url)*"#;
4604 let output = r#"<p><em>foo <a href="/url">bar</a></em></p>"#;
4605 run(input, output);
4606}
4607
4608#[test]
4609fn src_line_6824() {
4610 let input = r#"*foo
4611bar*"#;
4612 let output = r#"<p><em>foo
4613bar</em></p>"#;
4614 run(input, output);
4615}
4616
4617#[test]
4618fn src_line_6836() {
4619 let input = r#"_foo __bar__ baz_"#;
4620 let output = r#"<p><em>foo <strong>bar</strong> baz</em></p>"#;
4621 run(input, output);
4622}
4623
4624#[test]
4625fn src_line_6843() {
4626 let input = r#"_foo _bar_ baz_"#;
4627 let output = r#"<p><em>foo <em>bar</em> baz</em></p>"#;
4628 run(input, output);
4629}
4630
4631#[test]
4632fn src_line_6850() {
4633 let input = r#"__foo_ bar_"#;
4634 let output = r#"<p><em><em>foo</em> bar</em></p>"#;
4635 run(input, output);
4636}
4637
4638#[test]
4639fn src_line_6857() {
4640 let input = r#"*foo *bar**"#;
4641 let output = r#"<p><em>foo <em>bar</em></em></p>"#;
4642 run(input, output);
4643}
4644
4645#[test]
4646fn src_line_6864() {
4647 let input = r#"*foo **bar** baz*"#;
4648 let output = r#"<p><em>foo <strong>bar</strong> baz</em></p>"#;
4649 run(input, output);
4650}
4651
4652#[test]
4653fn src_line_6870() {
4654 let input = r#"*foo**bar**baz*"#;
4655 let output = r#"<p><em>foo<strong>bar</strong>baz</em></p>"#;
4656 run(input, output);
4657}
4658
4659#[test]
4660fn src_line_6894() {
4661 let input = r#"*foo**bar*"#;
4662 let output = r#"<p><em>foo**bar</em></p>"#;
4663 run(input, output);
4664}
4665
4666#[test]
4667fn src_line_6907() {
4668 let input = r#"***foo** bar*"#;
4669 let output = r#"<p><em><strong>foo</strong> bar</em></p>"#;
4670 run(input, output);
4671}
4672
4673#[test]
4674fn src_line_6914() {
4675 let input = r#"*foo **bar***"#;
4676 let output = r#"<p><em>foo <strong>bar</strong></em></p>"#;
4677 run(input, output);
4678}
4679
4680#[test]
4681fn src_line_6921() {
4682 let input = r#"*foo**bar***"#;
4683 let output = r#"<p><em>foo<strong>bar</strong></em></p>"#;
4684 run(input, output);
4685}
4686
4687#[test]
4688fn src_line_6932() {
4689 let input = r#"foo***bar***baz"#;
4690 let output = r#"<p>foo<em><strong>bar</strong></em>baz</p>"#;
4691 run(input, output);
4692}
4693
4694#[test]
4695fn src_line_6938() {
4696 let input = r#"foo******bar*********baz"#;
4697 let output = r#"<p>foo<strong><strong><strong>bar</strong></strong></strong>***baz</p>"#;
4698 run(input, output);
4699}
4700
4701#[test]
4702fn src_line_6947() {
4703 let input = r#"*foo **bar *baz* bim** bop*"#;
4704 let output = r#"<p><em>foo <strong>bar <em>baz</em> bim</strong> bop</em></p>"#;
4705 run(input, output);
4706}
4707
4708#[test]
4709fn src_line_6954() {
4710 let input = r#"*foo [*bar*](/url)*"#;
4711 let output = r#"<p><em>foo <a href="/url"><em>bar</em></a></em></p>"#;
4712 run(input, output);
4713}
4714
4715#[test]
4716fn src_line_6963() {
4717 let input = r#"** is not an empty emphasis"#;
4718 let output = r#"<p>** is not an empty emphasis</p>"#;
4719 run(input, output);
4720}
4721
4722#[test]
4723fn src_line_6970() {
4724 let input = r#"**** is not an empty strong emphasis"#;
4725 let output = r#"<p>**** is not an empty strong emphasis</p>"#;
4726 run(input, output);
4727}
4728
4729#[test]
4730fn src_line_6983() {
4731 let input = r#"**foo [bar](/url)**"#;
4732 let output = r#"<p><strong>foo <a href="/url">bar</a></strong></p>"#;
4733 run(input, output);
4734}
4735
4736#[test]
4737fn src_line_6990() {
4738 let input = r#"**foo
4739bar**"#;
4740 let output = r#"<p><strong>foo
4741bar</strong></p>"#;
4742 run(input, output);
4743}
4744
4745#[test]
4746fn src_line_7002() {
4747 let input = r#"__foo _bar_ baz__"#;
4748 let output = r#"<p><strong>foo <em>bar</em> baz</strong></p>"#;
4749 run(input, output);
4750}
4751
4752#[test]
4753fn src_line_7009() {
4754 let input = r#"__foo __bar__ baz__"#;
4755 let output = r#"<p><strong>foo <strong>bar</strong> baz</strong></p>"#;
4756 run(input, output);
4757}
4758
4759#[test]
4760fn src_line_7016() {
4761 let input = r#"____foo__ bar__"#;
4762 let output = r#"<p><strong><strong>foo</strong> bar</strong></p>"#;
4763 run(input, output);
4764}
4765
4766#[test]
4767fn src_line_7023() {
4768 let input = r#"**foo **bar****"#;
4769 let output = r#"<p><strong>foo <strong>bar</strong></strong></p>"#;
4770 run(input, output);
4771}
4772
4773#[test]
4774fn src_line_7030() {
4775 let input = r#"**foo *bar* baz**"#;
4776 let output = r#"<p><strong>foo <em>bar</em> baz</strong></p>"#;
4777 run(input, output);
4778}
4779
4780#[test]
4781fn src_line_7037() {
4782 let input = r#"**foo*bar*baz**"#;
4783 let output = r#"<p><strong>foo<em>bar</em>baz</strong></p>"#;
4784 run(input, output);
4785}
4786
4787#[test]
4788fn src_line_7044() {
4789 let input = r#"***foo* bar**"#;
4790 let output = r#"<p><strong><em>foo</em> bar</strong></p>"#;
4791 run(input, output);
4792}
4793
4794#[test]
4795fn src_line_7051() {
4796 let input = r#"**foo *bar***"#;
4797 let output = r#"<p><strong>foo <em>bar</em></strong></p>"#;
4798 run(input, output);
4799}
4800
4801#[test]
4802fn src_line_7060() {
4803 let input = r#"**foo *bar **baz**
4804bim* bop**"#;
4805 let output = r#"<p><strong>foo <em>bar <strong>baz</strong>
4806bim</em> bop</strong></p>"#;
4807 run(input, output);
4808}
4809
4810#[test]
4811fn src_line_7069() {
4812 let input = r#"**foo [*bar*](/url)**"#;
4813 let output = r#"<p><strong>foo <a href="/url"><em>bar</em></a></strong></p>"#;
4814 run(input, output);
4815}
4816
4817#[test]
4818fn src_line_7078() {
4819 let input = r#"__ is not an empty emphasis"#;
4820 let output = r#"<p>__ is not an empty emphasis</p>"#;
4821 run(input, output);
4822}
4823
4824#[test]
4825fn src_line_7085() {
4826 let input = r#"____ is not an empty strong emphasis"#;
4827 let output = r#"<p>____ is not an empty strong emphasis</p>"#;
4828 run(input, output);
4829}
4830
4831#[test]
4832fn src_line_7095() {
4833 let input = r#"foo ***"#;
4834 let output = r#"<p>foo ***</p>"#;
4835 run(input, output);
4836}
4837
4838#[test]
4839fn src_line_7102() {
4840 let input = r#"foo *\**"#;
4841 let output = r#"<p>foo <em>*</em></p>"#;
4842 run(input, output);
4843}
4844
4845#[test]
4846fn src_line_7109() {
4847 let input = r#"foo *_*"#;
4848 let output = r#"<p>foo <em>_</em></p>"#;
4849 run(input, output);
4850}
4851
4852#[test]
4853fn src_line_7116() {
4854 let input = r#"foo *****"#;
4855 let output = r#"<p>foo *****</p>"#;
4856 run(input, output);
4857}
4858
4859#[test]
4860fn src_line_7123() {
4861 let input = r#"foo **\***"#;
4862 let output = r#"<p>foo <strong>*</strong></p>"#;
4863 run(input, output);
4864}
4865
4866#[test]
4867fn src_line_7130() {
4868 let input = r#"foo **_**"#;
4869 let output = r#"<p>foo <strong>_</strong></p>"#;
4870 run(input, output);
4871}
4872
4873#[test]
4874fn src_line_7141() {
4875 let input = r#"**foo*"#;
4876 let output = r#"<p>*<em>foo</em></p>"#;
4877 run(input, output);
4878}
4879
4880#[test]
4881fn src_line_7148() {
4882 let input = r#"*foo**"#;
4883 let output = r#"<p><em>foo</em>*</p>"#;
4884 run(input, output);
4885}
4886
4887#[test]
4888fn src_line_7155() {
4889 let input = r#"***foo**"#;
4890 let output = r#"<p>*<strong>foo</strong></p>"#;
4891 run(input, output);
4892}
4893
4894#[test]
4895fn src_line_7162() {
4896 let input = r#"****foo*"#;
4897 let output = r#"<p>***<em>foo</em></p>"#;
4898 run(input, output);
4899}
4900
4901#[test]
4902fn src_line_7169() {
4903 let input = r#"**foo***"#;
4904 let output = r#"<p><strong>foo</strong>*</p>"#;
4905 run(input, output);
4906}
4907
4908#[test]
4909fn src_line_7176() {
4910 let input = r#"*foo****"#;
4911 let output = r#"<p><em>foo</em>***</p>"#;
4912 run(input, output);
4913}
4914
4915#[test]
4916fn src_line_7186() {
4917 let input = r#"foo ___"#;
4918 let output = r#"<p>foo ___</p>"#;
4919 run(input, output);
4920}
4921
4922#[test]
4923fn src_line_7193() {
4924 let input = r#"foo _\__"#;
4925 let output = r#"<p>foo <em>_</em></p>"#;
4926 run(input, output);
4927}
4928
4929#[test]
4930fn src_line_7200() {
4931 let input = r#"foo _*_"#;
4932 let output = r#"<p>foo <em>*</em></p>"#;
4933 run(input, output);
4934}
4935
4936#[test]
4937fn src_line_7207() {
4938 let input = r#"foo _____"#;
4939 let output = r#"<p>foo _____</p>"#;
4940 run(input, output);
4941}
4942
4943#[test]
4944fn src_line_7214() {
4945 let input = r#"foo __\___"#;
4946 let output = r#"<p>foo <strong>_</strong></p>"#;
4947 run(input, output);
4948}
4949
4950#[test]
4951fn src_line_7221() {
4952 let input = r#"foo __*__"#;
4953 let output = r#"<p>foo <strong>*</strong></p>"#;
4954 run(input, output);
4955}
4956
4957#[test]
4958fn src_line_7228() {
4959 let input = r#"__foo_"#;
4960 let output = r#"<p>_<em>foo</em></p>"#;
4961 run(input, output);
4962}
4963
4964#[test]
4965fn src_line_7239() {
4966 let input = r#"_foo__"#;
4967 let output = r#"<p><em>foo</em>_</p>"#;
4968 run(input, output);
4969}
4970
4971#[test]
4972fn src_line_7246() {
4973 let input = r#"___foo__"#;
4974 let output = r#"<p>_<strong>foo</strong></p>"#;
4975 run(input, output);
4976}
4977
4978#[test]
4979fn src_line_7253() {
4980 let input = r#"____foo_"#;
4981 let output = r#"<p>___<em>foo</em></p>"#;
4982 run(input, output);
4983}
4984
4985#[test]
4986fn src_line_7260() {
4987 let input = r#"__foo___"#;
4988 let output = r#"<p><strong>foo</strong>_</p>"#;
4989 run(input, output);
4990}
4991
4992#[test]
4993fn src_line_7267() {
4994 let input = r#"_foo____"#;
4995 let output = r#"<p><em>foo</em>___</p>"#;
4996 run(input, output);
4997}
4998
4999#[test]
5000fn src_line_7277() {
5001 let input = r#"**foo**"#;
5002 let output = r#"<p><strong>foo</strong></p>"#;
5003 run(input, output);
5004}
5005
5006#[test]
5007fn src_line_7284() {
5008 let input = r#"*_foo_*"#;
5009 let output = r#"<p><em><em>foo</em></em></p>"#;
5010 run(input, output);
5011}
5012
5013#[test]
5014fn src_line_7291() {
5015 let input = r#"__foo__"#;
5016 let output = r#"<p><strong>foo</strong></p>"#;
5017 run(input, output);
5018}
5019
5020#[test]
5021fn src_line_7298() {
5022 let input = r#"_*foo*_"#;
5023 let output = r#"<p><em><em>foo</em></em></p>"#;
5024 run(input, output);
5025}
5026
5027#[test]
5028fn src_line_7308() {
5029 let input = r#"****foo****"#;
5030 let output = r#"<p><strong><strong>foo</strong></strong></p>"#;
5031 run(input, output);
5032}
5033
5034#[test]
5035fn src_line_7315() {
5036 let input = r#"____foo____"#;
5037 let output = r#"<p><strong><strong>foo</strong></strong></p>"#;
5038 run(input, output);
5039}
5040
5041#[test]
5042fn src_line_7326() {
5043 let input = r#"******foo******"#;
5044 let output = r#"<p><strong><strong><strong>foo</strong></strong></strong></p>"#;
5045 run(input, output);
5046}
5047
5048#[test]
5049fn src_line_7335() {
5050 let input = r#"***foo***"#;
5051 let output = r#"<p><em><strong>foo</strong></em></p>"#;
5052 run(input, output);
5053}
5054
5055#[test]
5056fn src_line_7342() {
5057 let input = r#"_____foo_____"#;
5058 let output = r#"<p><em><strong><strong>foo</strong></strong></em></p>"#;
5059 run(input, output);
5060}
5061
5062#[test]
5063fn src_line_7351() {
5064 let input = r#"*foo _bar* baz_"#;
5065 let output = r#"<p><em>foo _bar</em> baz_</p>"#;
5066 run(input, output);
5067}
5068
5069#[test]
5070fn src_line_7358() {
5071 let input = r#"*foo __bar *baz bim__ bam*"#;
5072 let output = r#"<p><em>foo <strong>bar *baz bim</strong> bam</em></p>"#;
5073 run(input, output);
5074}
5075
5076#[test]
5077fn src_line_7367() {
5078 let input = r#"**foo **bar baz**"#;
5079 let output = r#"<p>**foo <strong>bar baz</strong></p>"#;
5080 run(input, output);
5081}
5082
5083#[test]
5084fn src_line_7374() {
5085 let input = r#"*foo *bar baz*"#;
5086 let output = r#"<p>*foo <em>bar baz</em></p>"#;
5087 run(input, output);
5088}
5089
5090#[test]
5091fn src_line_7383() {
5092 let input = r#"*[bar*](/url)"#;
5093 let output = r#"<p>*<a href="/url">bar*</a></p>"#;
5094 run(input, output);
5095}
5096
5097#[test]
5098fn src_line_7390() {
5099 let input = r#"_foo [bar_](/url)"#;
5100 let output = r#"<p>_foo <a href="/url">bar_</a></p>"#;
5101 run(input, output);
5102}
5103
5104#[test]
5105fn src_line_7397() {
5106 let input = r#"*<img src="foo" title="*"/>"#;
5107 let output = r#"<p>*<img src="foo" title="*"/></p>"#;
5108 run(input, output);
5109}
5110
5111#[test]
5112fn src_line_7404() {
5113 let input = r#"**<a href="**">"#;
5114 let output = r#"<p>**<a href="**"></p>"#;
5115 run(input, output);
5116}
5117
5118#[test]
5119fn src_line_7411() {
5120 let input = r#"__<a href="__">"#;
5121 let output = r#"<p>__<a href="__"></p>"#;
5122 run(input, output);
5123}
5124
5125#[test]
5126fn src_line_7418() {
5127 let input = r#"*a `*`*"#;
5128 let output = r#"<p><em>a <code>*</code></em></p>"#;
5129 run(input, output);
5130}
5131
5132#[test]
5133fn src_line_7425() {
5134 let input = r#"_a `_`_"#;
5135 let output = r#"<p><em>a <code>_</code></em></p>"#;
5136 run(input, output);
5137}
5138
5139#[test]
5140fn src_line_7432() {
5141 let input = r#"**a<http://foo.bar/?q=**>"#;
5142 let output = r#"<p>**a<a href="http://foo.bar/?q=**">http://foo.bar/?q=**</a></p>"#;
5143 run(input, output);
5144}
5145
5146#[test]
5147fn src_line_7439() {
5148 let input = r#"__a<http://foo.bar/?q=__>"#;
5149 let output = r#"<p>__a<a href="http://foo.bar/?q=__">http://foo.bar/?q=__</a></p>"#;
5150 run(input, output);
5151}
5152
5153#[test]
5154fn src_line_7527() {
5155 let input = r#"[link](/uri "title")"#;
5156 let output = r#"<p><a href="/uri" title="title">link</a></p>"#;
5157 run(input, output);
5158}
5159
5160#[test]
5161fn src_line_7537() {
5162 let input = r#"[link](/uri)"#;
5163 let output = r#"<p><a href="/uri">link</a></p>"#;
5164 run(input, output);
5165}
5166
5167#[test]
5168fn src_line_7543() {
5169 let input = r#"[](./target.md)"#;
5170 let output = r#"<p><a href="./target.md"></a></p>"#;
5171 run(input, output);
5172}
5173
5174#[test]
5175fn src_line_7550() {
5176 let input = r#"[link]()"#;
5177 let output = r#"<p><a href="">link</a></p>"#;
5178 run(input, output);
5179}
5180
5181#[test]
5182fn src_line_7557() {
5183 let input = r#"[link](<>)"#;
5184 let output = r#"<p><a href="">link</a></p>"#;
5185 run(input, output);
5186}
5187
5188#[test]
5189fn src_line_7564() {
5190 let input = r#"[]()"#;
5191 let output = r#"<p><a href=""></a></p>"#;
5192 run(input, output);
5193}
5194
5195#[test]
5196fn src_line_7573() {
5197 let input = r#"[link](/my uri)"#;
5198 let output = r#"<p>[link](/my uri)</p>"#;
5199 run(input, output);
5200}
5201
5202#[test]
5203fn src_line_7579() {
5204 let input = r#"[link](</my uri>)"#;
5205 let output = r#"<p><a href="/my%20uri">link</a></p>"#;
5206 run(input, output);
5207}
5208
5209#[test]
5210fn src_line_7588() {
5211 let input = r#"[link](foo
5212bar)"#;
5213 let output = r#"<p>[link](foo
5214bar)</p>"#;
5215 run(input, output);
5216}
5217
5218#[test]
5219fn src_line_7596() {
5220 let input = r#"[link](<foo
5221bar>)"#;
5222 let output = r#"<p>[link](<foo
5223bar>)</p>"#;
5224 run(input, output);
5225}
5226
5227#[test]
5228fn src_line_7607() {
5229 let input = r#"[a](<b)c>)"#;
5230 let output = r#"<p><a href="b)c">a</a></p>"#;
5231 run(input, output);
5232}
5233
5234#[test]
5235fn src_line_7615() {
5236 let input = r#"[link](<foo\>)"#;
5237 let output = r#"<p>[link](&lt;foo&gt;)</p>"#;
5238 run(input, output);
5239}
5240
5241#[test]
5242fn src_line_7624() {
5243 let input = r#"[a](<b)c
5244[a](<b)c>
5245[a](<b>c)"#;
5246 let output = r#"<p>[a](&lt;b)c
5247[a](&lt;b)c&gt;
5248[a](<b>c)</p>"#;
5249 run(input, output);
5250}
5251
5252#[test]
5253fn src_line_7636() {
5254 let input = r#"[link](\(foo\))"#;
5255 let output = r#"<p><a href="(foo)">link</a></p>"#;
5256 run(input, output);
5257}
5258
5259#[test]
5260fn src_line_7645() {
5261 let input = r#"[link](foo(and(bar)))"#;
5262 let output = r#"<p><a href="foo(and(bar))">link</a></p>"#;
5263 run(input, output);
5264}
5265
5266#[test]
5267fn src_line_7654() {
5268 let input = r#"[link](foo(and(bar))"#;
5269 let output = r#"<p>[link](foo(and(bar))</p>"#;
5270 run(input, output);
5271}
5272
5273#[test]
5274fn src_line_7661() {
5275 let input = r#"[link](foo\(and\(bar\))"#;
5276 let output = r#"<p><a href="foo(and(bar)">link</a></p>"#;
5277 run(input, output);
5278}
5279
5280#[test]
5281fn src_line_7668() {
5282 let input = r#"[link](<foo(and(bar)>)"#;
5283 let output = r#"<p><a href="foo(and(bar)">link</a></p>"#;
5284 run(input, output);
5285}
5286
5287#[test]
5288fn src_line_7678() {
5289 let input = r#"[link](foo\)\:)"#;
5290 let output = r#"<p><a href="foo):">link</a></p>"#;
5291 run(input, output);
5292}
5293
5294#[test]
5295fn src_line_7687() {
5296 let input = r#"[link](#fragment)
5297
5298[link](http://example.com#fragment)
5299
5300[link](http://example.com?foo=3#frag)"#;
5301 let output = r##"<p><a href="#fragment">link</a></p>
5302<p><a href="http://example.com#fragment">link</a></p>
5303<p><a href="http://example.com?foo=3#frag">link</a></p>"##;
5304 run(input, output);
5305}
5306
5307#[test]
5308fn src_line_7703() {
5309 let input = r#"[link](foo\bar)"#;
5310 let output = r#"<p><a href="foo%5Cbar">link</a></p>"#;
5311 run(input, output);
5312}
5313
5314#[test]
5315fn src_line_7719() {
5316 let input = r#"[link](foo%20b&auml;)"#;
5317 let output = r#"<p><a href="foo%20b%C3%A4">link</a></p>"#;
5318 run(input, output);
5319}
5320
5321#[test]
5322fn src_line_7730() {
5323 let input = r#"[link]("title")"#;
5324 let output = r#"<p><a href="%22title%22">link</a></p>"#;
5325 run(input, output);
5326}
5327
5328#[test]
5329fn src_line_7739() {
5330 let input = r#"[link](/url "title")
5331[link](/url 'title')
5332[link](/url (title))"#;
5333 let output = r#"<p><a href="/url" title="title">link</a>
5334<a href="/url" title="title">link</a>
5335<a href="/url" title="title">link</a></p>"#;
5336 run(input, output);
5337}
5338
5339#[test]
5340fn src_line_7753() {
5341 let input = r#"[link](/url "title \"&quot;")"#;
5342 let output = r#"<p><a href="/url" title="title &quot;&quot;">link</a></p>"#;
5343 run(input, output);
5344}
5345
5346#[test]
5347fn src_line_7764() {
5348 let input = r#"[link](/url "title")"#;
5349 let output = r#"<p><a href="/url%C2%A0%22title%22">link</a></p>"#;
5350 run(input, output);
5351}
5352
5353#[test]
5354fn src_line_7773() {
5355 let input = r#"[link](/url "title "and" title")"#;
5356 let output = r#"<p>[link](/url &quot;title &quot;and&quot; title&quot;)</p>"#;
5357 run(input, output);
5358}
5359
5360#[test]
5361fn src_line_7782() {
5362 let input = r#"[link](/url 'title "and" title')"#;
5363 let output = r#"<p><a href="/url" title="title &quot;and&quot; title">link</a></p>"#;
5364 run(input, output);
5365}
5366
5367#[test]
5368fn src_line_7807() {
5369 let input = r#"[link]( /uri
5370 "title" )"#;
5371 let output = r#"<p><a href="/uri" title="title">link</a></p>"#;
5372 run(input, output);
5373}
5374
5375#[test]
5376fn src_line_7818() {
5377 let input = r#"[link] (/uri)"#;
5378 let output = r#"<p>[link] (/uri)</p>"#;
5379 run(input, output);
5380}
5381
5382#[test]
5383fn src_line_7828() {
5384 let input = r#"[link [foo [bar]]](/uri)"#;
5385 let output = r#"<p><a href="/uri">link [foo [bar]]</a></p>"#;
5386 run(input, output);
5387}
5388
5389#[test]
5390fn src_line_7835() {
5391 let input = r#"[link] bar](/uri)"#;
5392 let output = r#"<p>[link] bar](/uri)</p>"#;
5393 run(input, output);
5394}
5395
5396#[test]
5397fn src_line_7842() {
5398 let input = r#"[link [bar](/uri)"#;
5399 let output = r#"<p>[link <a href="/uri">bar</a></p>"#;
5400 run(input, output);
5401}
5402
5403#[test]
5404fn src_line_7849() {
5405 let input = r#"[link \[bar](/uri)"#;
5406 let output = r#"<p><a href="/uri">link [bar</a></p>"#;
5407 run(input, output);
5408}
5409
5410#[test]
5411fn src_line_7858() {
5412 let input = r#"[link *foo **bar** `#`*](/uri)"#;
5413 let output = r#"<p><a href="/uri">link <em>foo <strong>bar</strong> <code>#</code></em></a></p>"#;
5414 run(input, output);
5415}
5416
5417#[test]
5418fn src_line_7865() {
5419 let input = r#"[![moon](moon.jpg)](/uri)"#;
5420 let output = r#"<p><a href="/uri"><img src="moon.jpg" alt="moon" /></a></p>"#;
5421 run(input, output);
5422}
5423
5424#[test]
5425fn src_line_7874() {
5426 let input = r#"[foo [bar](/uri)](/uri)"#;
5427 let output = r#"<p>[foo <a href="/uri">bar</a>](/uri)</p>"#;
5428 run(input, output);
5429}
5430
5431#[test]
5432fn src_line_7881() {
5433 let input = r#"[foo *[bar [baz](/uri)](/uri)*](/uri)"#;
5434 let output = r#"<p>[foo <em>[bar <a href="/uri">baz</a>](/uri)</em>](/uri)</p>"#;
5435 run(input, output);
5436}
5437
5438#[test]
5439fn src_line_7888() {
5440 let input = r#"![[[foo](uri1)](uri2)](uri3)"#;
5441 let output = r#"<p><img src="uri3" alt="[foo](uri2)" /></p>"#;
5442 run(input, output);
5443}
5444
5445#[test]
5446fn src_line_7898() {
5447 let input = r#"*[foo*](/uri)"#;
5448 let output = r#"<p>*<a href="/uri">foo*</a></p>"#;
5449 run(input, output);
5450}
5451
5452#[test]
5453fn src_line_7905() {
5454 let input = r#"[foo *bar](baz*)"#;
5455 let output = r#"<p><a href="baz*">foo *bar</a></p>"#;
5456 run(input, output);
5457}
5458
5459#[test]
5460fn src_line_7915() {
5461 let input = r#"*foo [bar* baz]"#;
5462 let output = r#"<p><em>foo [bar</em> baz]</p>"#;
5463 run(input, output);
5464}
5465
5466#[test]
5467fn src_line_7925() {
5468 let input = r#"[foo <bar attr="](baz)">"#;
5469 let output = r#"<p>[foo <bar attr="](baz)"></p>"#;
5470 run(input, output);
5471}
5472
5473#[test]
5474fn src_line_7932() {
5475 let input = r#"[foo`](/uri)`"#;
5476 let output = r#"<p>[foo<code>](/uri)</code></p>"#;
5477 run(input, output);
5478}
5479
5480#[test]
5481fn src_line_7939() {
5482 let input = r#"[foo<http://example.com/?search=](uri)>"#;
5483 let output = r#"<p>[foo<a href="http://example.com/?search=%5D(uri)">http://example.com/?search=](uri)</a></p>"#;
5484 run(input, output);
5485}
5486
5487#[test]
5488fn src_line_7977() {
5489 let input = r#"[foo][bar]
5490
5491[bar]: /url "title""#;
5492 let output = r#"<p><a href="/url" title="title">foo</a></p>"#;
5493 run(input, output);
5494}
5495
5496#[test]
5497fn src_line_7992() {
5498 let input = r#"[link [foo [bar]]][ref]
5499
5500[ref]: /uri"#;
5501 let output = r#"<p><a href="/uri">link [foo [bar]]</a></p>"#;
5502 run(input, output);
5503}
5504
5505#[test]
5506fn src_line_8001() {
5507 let input = r#"[link \[bar][ref]
5508
5509[ref]: /uri"#;
5510 let output = r#"<p><a href="/uri">link [bar</a></p>"#;
5511 run(input, output);
5512}
5513
5514#[test]
5515fn src_line_8012() {
5516 let input = r#"[link *foo **bar** `#`*][ref]
5517
5518[ref]: /uri"#;
5519 let output = r#"<p><a href="/uri">link <em>foo <strong>bar</strong> <code>#</code></em></a></p>"#;
5520 run(input, output);
5521}
5522
5523#[test]
5524fn src_line_8021() {
5525 let input = r#"[![moon](moon.jpg)][ref]
5526
5527[ref]: /uri"#;
5528 let output = r#"<p><a href="/uri"><img src="moon.jpg" alt="moon" /></a></p>"#;
5529 run(input, output);
5530}
5531
5532#[test]
5533fn src_line_8032() {
5534 let input = r#"[foo [bar](/uri)][ref]
5535
5536[ref]: /uri"#;
5537 let output = r#"<p>[foo <a href="/uri">bar</a>]<a href="/uri">ref</a></p>"#;
5538 run(input, output);
5539}
5540
5541#[test]
5542fn src_line_8041() {
5543 let input = r#"[foo *bar [baz][ref]*][ref]
5544
5545[ref]: /uri"#;
5546 let output = r#"<p>[foo <em>bar <a href="/uri">baz</a></em>]<a href="/uri">ref</a></p>"#;
5547 run(input, output);
5548}
5549
5550#[test]
5551fn src_line_8056() {
5552 let input = r#"*[foo*][ref]
5553
5554[ref]: /uri"#;
5555 let output = r#"<p>*<a href="/uri">foo*</a></p>"#;
5556 run(input, output);
5557}
5558
5559#[test]
5560fn src_line_8065() {
5561 let input = r#"[foo *bar][ref]*
5562
5563[ref]: /uri"#;
5564 let output = r#"<p><a href="/uri">foo *bar</a>*</p>"#;
5565 run(input, output);
5566}
5567
5568#[test]
5569fn src_line_8077() {
5570 let input = r#"[foo <bar attr="][ref]">
5571
5572[ref]: /uri"#;
5573 let output = r#"<p>[foo <bar attr="][ref]"></p>"#;
5574 run(input, output);
5575}
5576
5577#[test]
5578fn src_line_8086() {
5579 let input = r#"[foo`][ref]`
5580
5581[ref]: /uri"#;
5582 let output = r#"<p>[foo<code>][ref]</code></p>"#;
5583 run(input, output);
5584}
5585
5586#[test]
5587fn src_line_8095() {
5588 let input = r#"[foo<http://example.com/?search=][ref]>
5589
5590[ref]: /uri"#;
5591 let output = r#"<p>[foo<a href="http://example.com/?search=%5D%5Bref%5D">http://example.com/?search=][ref]</a></p>"#;
5592 run(input, output);
5593}
5594
5595#[test]
5596fn src_line_8106() {
5597 let input = r#"[foo][BaR]
5598
5599[bar]: /url "title""#;
5600 let output = r#"<p><a href="/url" title="title">foo</a></p>"#;
5601 run(input, output);
5602}
5603
5604#[test]
5605fn src_line_8117() {
5606 let input = r#"[ẞ]
5607
5608[SS]: /url"#;
5609 let output = r#"<p><a href="/url">ẞ</a></p>"#;
5610 run(input, output);
5611}
5612
5613#[test]
5614fn src_line_8129() {
5615 let input = r#"[Foo
5616 bar]: /url
5617
5618[Baz][Foo bar]"#;
5619 let output = r#"<p><a href="/url">Baz</a></p>"#;
5620 run(input, output);
5621}
5622
5623#[test]
5624fn src_line_8142() {
5625 let input = r#"[foo] [bar]
5626
5627[bar]: /url "title""#;
5628 let output = r#"<p>[foo] <a href="/url" title="title">bar</a></p>"#;
5629 run(input, output);
5630}
5631
5632#[test]
5633fn src_line_8151() {
5634 let input = r#"[foo]
5635[bar]
5636
5637[bar]: /url "title""#;
5638 let output = r#"<p>[foo]
5639<a href="/url" title="title">bar</a></p>"#;
5640 run(input, output);
5641}
5642
5643#[test]
5644fn src_line_8192() {
5645 let input = r#"[foo]: /url1
5646
5647[foo]: /url2
5648
5649[bar][foo]"#;
5650 let output = r#"<p><a href="/url1">bar</a></p>"#;
5651 run(input, output);
5652}
5653
5654#[test]
5655fn src_line_8207() {
5656 let input = r#"[bar][foo\!]
5657
5658[foo!]: /url"#;
5659 let output = r#"<p>[bar][foo!]</p>"#;
5660 run(input, output);
5661}
5662
5663#[test]
5664fn src_line_8219() {
5665 let input = r#"[foo][ref[]
5666
5667[ref[]: /uri"#;
5668 let output = r#"<p>[foo][ref[]</p>
5669<p>[ref[]: /uri</p>"#;
5670 run(input, output);
5671}
5672
5673#[test]
5674fn src_line_8229() {
5675 let input = r#"[foo][ref[bar]]
5676
5677[ref[bar]]: /uri"#;
5678 let output = r#"<p>[foo][ref[bar]]</p>
5679<p>[ref[bar]]: /uri</p>"#;
5680 run(input, output);
5681}
5682
5683#[test]
5684fn src_line_8239() {
5685 let input = r#"[[[foo]]]
5686
5687[[[foo]]]: /url"#;
5688 let output = r#"<p>[[[foo]]]</p>
5689<p>[[[foo]]]: /url</p>"#;
5690 run(input, output);
5691}
5692
5693#[test]
5694fn src_line_8249() {
5695 let input = r#"[foo][ref\[]
5696
5697[ref\[]: /uri"#;
5698 let output = r#"<p><a href="/uri">foo</a></p>"#;
5699 run(input, output);
5700}
5701
5702#[test]
5703fn src_line_8260() {
5704 let input = r#"[bar\\]: /uri
5705
5706[bar\\]"#;
5707 let output = r#"<p><a href="/uri">bar\</a></p>"#;
5708 run(input, output);
5709}
5710
5711#[test]
5712fn src_line_8272() {
5713 let input = r#"[]
5714
5715[]: /uri"#;
5716 let output = r#"<p>[]</p>
5717<p>[]: /uri</p>"#;
5718 run(input, output);
5719}
5720
5721#[test]
5722fn src_line_8282() {
5723 let input = r#"[
5724 ]
5725
5726[
5727 ]: /uri"#;
5728 let output = r#"<p>[
5729]</p>
5730<p>[
5731]: /uri</p>"#;
5732 run(input, output);
5733}
5734
5735#[test]
5736fn src_line_8305() {
5737 let input = r#"[foo][]
5738
5739[foo]: /url "title""#;
5740 let output = r#"<p><a href="/url" title="title">foo</a></p>"#;
5741 run(input, output);
5742}
5743
5744#[test]
5745fn src_line_8314() {
5746 let input = r#"[*foo* bar][]
5747
5748[*foo* bar]: /url "title""#;
5749 let output = r#"<p><a href="/url" title="title"><em>foo</em> bar</a></p>"#;
5750 run(input, output);
5751}
5752
5753#[test]
5754fn src_line_8325() {
5755 let input = r#"[Foo][]
5756
5757[foo]: /url "title""#;
5758 let output = r#"<p><a href="/url" title="title">Foo</a></p>"#;
5759 run(input, output);
5760}
5761
5762#[test]
5763fn src_line_8338() {
5764 let input = "[foo]\x20
5765[]
5766
5767[foo]: /url \"title\"";
5768 let output = r#"<p><a href="/url" title="title">foo</a>
5769[]</p>"#;
5770 run(input, output);
5771}
5772
5773#[test]
5774fn src_line_8358() {
5775 let input = r#"[foo]
5776
5777[foo]: /url "title""#;
5778 let output = r#"<p><a href="/url" title="title">foo</a></p>"#;
5779 run(input, output);
5780}
5781
5782#[test]
5783fn src_line_8367() {
5784 let input = r#"[*foo* bar]
5785
5786[*foo* bar]: /url "title""#;
5787 let output = r#"<p><a href="/url" title="title"><em>foo</em> bar</a></p>"#;
5788 run(input, output);
5789}
5790
5791#[test]
5792fn src_line_8376() {
5793 let input = r#"[[*foo* bar]]
5794
5795[*foo* bar]: /url "title""#;
5796 let output = r#"<p>[<a href="/url" title="title"><em>foo</em> bar</a>]</p>"#;
5797 run(input, output);
5798}
5799
5800#[test]
5801fn src_line_8385() {
5802 let input = r#"[[bar [foo]
5803
5804[foo]: /url"#;
5805 let output = r#"<p>[[bar <a href="/url">foo</a></p>"#;
5806 run(input, output);
5807}
5808
5809#[test]
5810fn src_line_8396() {
5811 let input = r#"[Foo]
5812
5813[foo]: /url "title""#;
5814 let output = r#"<p><a href="/url" title="title">Foo</a></p>"#;
5815 run(input, output);
5816}
5817
5818#[test]
5819fn src_line_8407() {
5820 let input = r#"[foo] bar
5821
5822[foo]: /url"#;
5823 let output = r#"<p><a href="/url">foo</a> bar</p>"#;
5824 run(input, output);
5825}
5826
5827#[test]
5828fn src_line_8419() {
5829 let input = r#"\[foo]
5830
5831[foo]: /url "title""#;
5832 let output = r#"<p>[foo]</p>"#;
5833 run(input, output);
5834}
5835
5836#[test]
5837fn src_line_8431() {
5838 let input = r#"[foo*]: /url
5839
5840*[foo*]"#;
5841 let output = r#"<p>*<a href="/url">foo*</a></p>"#;
5842 run(input, output);
5843}
5844
5845#[test]
5846fn src_line_8443() {
5847 let input = r#"[foo][bar]
5848
5849[foo]: /url1
5850[bar]: /url2"#;
5851 let output = r#"<p><a href="/url2">foo</a></p>"#;
5852 run(input, output);
5853}
5854
5855#[test]
5856fn src_line_8452() {
5857 let input = r#"[foo][]
5858
5859[foo]: /url1"#;
5860 let output = r#"<p><a href="/url1">foo</a></p>"#;
5861 run(input, output);
5862}
5863
5864#[test]
5865fn src_line_8462() {
5866 let input = r#"[foo]()
5867
5868[foo]: /url1"#;
5869 let output = r#"<p><a href="">foo</a></p>"#;
5870 run(input, output);
5871}
5872
5873#[test]
5874fn src_line_8470() {
5875 let input = r#"[foo](not a link)
5876
5877[foo]: /url1"#;
5878 let output = r#"<p><a href="/url1">foo</a>(not a link)</p>"#;
5879 run(input, output);
5880}
5881
5882#[test]
5883fn src_line_8481() {
5884 let input = r#"[foo][bar][baz]
5885
5886[baz]: /url"#;
5887 let output = r#"<p>[foo]<a href="/url">bar</a></p>"#;
5888 run(input, output);
5889}
5890
5891#[test]
5892fn src_line_8493() {
5893 let input = r#"[foo][bar][baz]
5894
5895[baz]: /url1
5896[bar]: /url2"#;
5897 let output = r#"<p><a href="/url2">foo</a><a href="/url1">baz</a></p>"#;
5898 run(input, output);
5899}
5900
5901#[test]
5902fn src_line_8506() {
5903 let input = r#"[foo][bar][baz]
5904
5905[baz]: /url1
5906[foo]: /url2"#;
5907 let output = r#"<p>[foo]<a href="/url1">bar</a></p>"#;
5908 run(input, output);
5909}
5910
5911#[test]
5912fn src_line_8529() {
5913 let input = r#"![foo](/url "title")"#;
5914 let output = r#"<p><img src="/url" alt="foo" title="title" /></p>"#;
5915 run(input, output);
5916}
5917
5918#[test]
5919fn src_line_8536() {
5920 let input = r#"![foo *bar*]
5921
5922[foo *bar*]: train.jpg "train & tracks""#;
5923 let output = r#"<p><img src="train.jpg" alt="foo bar" title="train &amp; tracks" /></p>"#;
5924 run(input, output);
5925}
5926
5927#[test]
5928fn src_line_8545() {
5929 let input = r#"![foo ![bar](/url)](/url2)"#;
5930 let output = r#"<p><img src="/url2" alt="foo bar" /></p>"#;
5931 run(input, output);
5932}
5933
5934#[test]
5935fn src_line_8552() {
5936 let input = r#"![foo [bar](/url)](/url2)"#;
5937 let output = r#"<p><img src="/url2" alt="foo bar" /></p>"#;
5938 run(input, output);
5939}
5940
5941#[test]
5942fn src_line_8566() {
5943 let input = r#"![foo *bar*][]
5944
5945[foo *bar*]: train.jpg "train & tracks""#;
5946 let output = r#"<p><img src="train.jpg" alt="foo bar" title="train &amp; tracks" /></p>"#;
5947 run(input, output);
5948}
5949
5950#[test]
5951fn src_line_8575() {
5952 let input = r#"![foo *bar*][foobar]
5953
5954[FOOBAR]: train.jpg "train & tracks""#;
5955 let output = r#"<p><img src="train.jpg" alt="foo bar" title="train &amp; tracks" /></p>"#;
5956 run(input, output);
5957}
5958
5959#[test]
5960fn src_line_8584() {
5961 let input = r#"![foo](train.jpg)"#;
5962 let output = r#"<p><img src="train.jpg" alt="foo" /></p>"#;
5963 run(input, output);
5964}
5965
5966#[test]
5967fn src_line_8591() {
5968 let input = r#"My ![foo bar](/path/to/train.jpg "title" )"#;
5969 let output = r#"<p>My <img src="/path/to/train.jpg" alt="foo bar" title="title" /></p>"#;
5970 run(input, output);
5971}
5972
5973#[test]
5974fn src_line_8598() {
5975 let input = r#"![foo](<url>)"#;
5976 let output = r#"<p><img src="url" alt="foo" /></p>"#;
5977 run(input, output);
5978}
5979
5980#[test]
5981fn src_line_8605() {
5982 let input = r#"![](/url)"#;
5983 let output = r#"<p><img src="/url" alt="" /></p>"#;
5984 run(input, output);
5985}
5986
5987#[test]
5988fn src_line_8614() {
5989 let input = r#"![foo][bar]
5990
5991[bar]: /url"#;
5992 let output = r#"<p><img src="/url" alt="foo" /></p>"#;
5993 run(input, output);
5994}
5995
5996#[test]
5997fn src_line_8623() {
5998 let input = r#"![foo][bar]
5999
6000[BAR]: /url"#;
6001 let output = r#"<p><img src="/url" alt="foo" /></p>"#;
6002 run(input, output);
6003}
6004
6005#[test]
6006fn src_line_8634() {
6007 let input = r#"![foo][]
6008
6009[foo]: /url "title""#;
6010 let output = r#"<p><img src="/url" alt="foo" title="title" /></p>"#;
6011 run(input, output);
6012}
6013
6014#[test]
6015fn src_line_8643() {
6016 let input = r#"![*foo* bar][]
6017
6018[*foo* bar]: /url "title""#;
6019 let output = r#"<p><img src="/url" alt="foo bar" title="title" /></p>"#;
6020 run(input, output);
6021}
6022
6023#[test]
6024fn src_line_8654() {
6025 let input = r#"![Foo][]
6026
6027[foo]: /url "title""#;
6028 let output = r#"<p><img src="/url" alt="Foo" title="title" /></p>"#;
6029 run(input, output);
6030}
6031
6032#[test]
6033fn src_line_8666() {
6034 let input = "![foo]\x20
6035[]
6036
6037[foo]: /url \"title\"";
6038 let output = r#"<p><img src="/url" alt="foo" title="title" />
6039[]</p>"#;
6040 run(input, output);
6041}
6042
6043#[test]
6044fn src_line_8679() {
6045 let input = r#"![foo]
6046
6047[foo]: /url "title""#;
6048 let output = r#"<p><img src="/url" alt="foo" title="title" /></p>"#;
6049 run(input, output);
6050}
6051
6052#[test]
6053fn src_line_8688() {
6054 let input = r#"![*foo* bar]
6055
6056[*foo* bar]: /url "title""#;
6057 let output = r#"<p><img src="/url" alt="foo bar" title="title" /></p>"#;
6058 run(input, output);
6059}
6060
6061#[test]
6062fn src_line_8699() {
6063 let input = r#"![[foo]]
6064
6065[[foo]]: /url "title""#;
6066 let output = r#"<p>![[foo]]</p>
6067<p>[[foo]]: /url &quot;title&quot;</p>"#;
6068 run(input, output);
6069}
6070
6071#[test]
6072fn src_line_8711() {
6073 let input = r#"![Foo]
6074
6075[foo]: /url "title""#;
6076 let output = r#"<p><img src="/url" alt="Foo" title="title" /></p>"#;
6077 run(input, output);
6078}
6079
6080#[test]
6081fn src_line_8723() {
6082 let input = r#"!\[foo]
6083
6084[foo]: /url "title""#;
6085 let output = r#"<p>![foo]</p>"#;
6086 run(input, output);
6087}
6088
6089#[test]
6090fn src_line_8735() {
6091 let input = r#"\![foo]
6092
6093[foo]: /url "title""#;
6094 let output = r#"<p>!<a href="/url" title="title">foo</a></p>"#;
6095 run(input, output);
6096}
6097
6098#[test]
6099fn src_line_8768() {
6100 let input = r#"<http://foo.bar.baz>"#;
6101 let output = r#"<p><a href="http://foo.bar.baz">http://foo.bar.baz</a></p>"#;
6102 run(input, output);
6103}
6104
6105#[test]
6106fn src_line_8775() {
6107 let input = r#"<http://foo.bar.baz/test?q=hello&id=22&boolean>"#;
6108 let output = r#"<p><a href="http://foo.bar.baz/test?q=hello&amp;id=22&amp;boolean">http://foo.bar.baz/test?q=hello&amp;id=22&amp;boolean</a></p>"#;
6109 run(input, output);
6110}
6111
6112#[test]
6113fn src_line_8782() {
6114 let input = r#"<irc://foo.bar:2233/baz>"#;
6115 let output = r#"<p><a href="irc://foo.bar:2233/baz">irc://foo.bar:2233/baz</a></p>"#;
6116 run(input, output);
6117}
6118
6119#[test]
6120fn src_line_8791() {
6121 let input = r#"<MAILTO:FOO@BAR.BAZ>"#;
6122 let output = r#"<p><a href="MAILTO:FOO@BAR.BAZ">MAILTO:FOO@BAR.BAZ</a></p>"#;
6123 run(input, output);
6124}
6125
6126#[test]
6127fn src_line_8803() {
6128 let input = r#"<a+b+c:d>"#;
6129 let output = r#"<p><a href="a+b+c:d">a+b+c:d</a></p>"#;
6130 run(input, output);
6131}
6132
6133#[test]
6134fn src_line_8810() {
6135 let input = r#"<made-up-scheme://foo,bar>"#;
6136 let output = r#"<p><a href="made-up-scheme://foo,bar">made-up-scheme://foo,bar</a></p>"#;
6137 run(input, output);
6138}
6139
6140#[test]
6141fn src_line_8817() {
6142 let input = r#"<http://../>"#;
6143 let output = r#"<p><a href="http://../">http://../</a></p>"#;
6144 run(input, output);
6145}
6146
6147#[test]
6148fn src_line_8824() {
6149 let input = r#"<localhost:5001/foo>"#;
6150 let output = r#"<p><a href="localhost:5001/foo">localhost:5001/foo</a></p>"#;
6151 run(input, output);
6152}
6153
6154#[test]
6155fn src_line_8833() {
6156 let input = r#"<http://foo.bar/baz bim>"#;
6157 let output = r#"<p>&lt;http://foo.bar/baz bim&gt;</p>"#;
6158 run(input, output);
6159}
6160
6161#[test]
6162fn src_line_8842() {
6163 let input = r#"<http://example.com/\[\>"#;
6164 let output = r#"<p><a href="http://example.com/%5C%5B%5C">http://example.com/\[\</a></p>"#;
6165 run(input, output);
6166}
6167
6168#[test]
6169fn src_line_8864() {
6170 let input = r#"<foo@bar.example.com>"#;
6171 let output = r#"<p><a href="mailto:foo@bar.example.com">foo@bar.example.com</a></p>"#;
6172 run(input, output);
6173}
6174
6175#[test]
6176fn src_line_8871() {
6177 let input = r#"<foo+special@Bar.baz-bar0.com>"#;
6178 let output = r#"<p><a href="mailto:foo+special@Bar.baz-bar0.com">foo+special@Bar.baz-bar0.com</a></p>"#;
6179 run(input, output);
6180}
6181
6182#[test]
6183fn src_line_8880() {
6184 let input = r#"<foo\+@bar.example.com>"#;
6185 let output = r#"<p>&lt;foo+@bar.example.com&gt;</p>"#;
6186 run(input, output);
6187}
6188
6189#[test]
6190fn src_line_8889() {
6191 let input = r#"<>"#;
6192 let output = r#"<p>&lt;&gt;</p>"#;
6193 run(input, output);
6194}
6195
6196#[test]
6197fn src_line_8896() {
6198 let input = r#"< http://foo.bar >"#;
6199 let output = r#"<p>&lt; http://foo.bar &gt;</p>"#;
6200 run(input, output);
6201}
6202
6203#[test]
6204fn src_line_8903() {
6205 let input = r#"<m:abc>"#;
6206 let output = r#"<p>&lt;m:abc&gt;</p>"#;
6207 run(input, output);
6208}
6209
6210#[test]
6211fn src_line_8910() {
6212 let input = r#"<foo.bar.baz>"#;
6213 let output = r#"<p>&lt;foo.bar.baz&gt;</p>"#;
6214 run(input, output);
6215}
6216
6217#[test]
6218fn src_line_8917() {
6219 let input = r#"http://example.com"#;
6220 let output = r#"<p>http://example.com</p>"#;
6221 run(input, output);
6222}
6223
6224#[test]
6225fn src_line_8924() {
6226 let input = r#"foo@bar.example.com"#;
6227 let output = r#"<p>foo@bar.example.com</p>"#;
6228 run(input, output);
6229}
6230
6231#[test]
6232fn src_line_9005() {
6233 let input = r#"<a><bab><c2c>"#;
6234 let output = r#"<p><a><bab><c2c></p>"#;
6235 run(input, output);
6236}
6237
6238#[test]
6239fn src_line_9014() {
6240 let input = r#"<a/><b2/>"#;
6241 let output = r#"<p><a/><b2/></p>"#;
6242 run(input, output);
6243}
6244
6245#[test]
6246fn src_line_9023() {
6247 let input = r#"<a /><b2
6248data="foo" >"#;
6249 let output = r#"<p><a /><b2
6250data="foo" ></p>"#;
6251 run(input, output);
6252}
6253
6254#[test]
6255fn src_line_9034() {
6256 let input = r#"<a foo="bar" bam = 'baz <em>"</em>'
6257_boolean zoop:33=zoop:33 />"#;
6258 let output = r#"<p><a foo="bar" bam = 'baz <em>"</em>'
6259_boolean zoop:33=zoop:33 /></p>"#;
6260 run(input, output);
6261}
6262
6263#[test]
6264fn src_line_9045() {
6265 let input = r#"Foo <responsive-image src="foo.jpg" />"#;
6266 let output = r#"<p>Foo <responsive-image src="foo.jpg" /></p>"#;
6267 run(input, output);
6268}
6269
6270#[test]
6271fn src_line_9054() {
6272 let input = r#"<33> <__>"#;
6273 let output = r#"<p>&lt;33&gt; &lt;__&gt;</p>"#;
6274 run(input, output);
6275}
6276
6277#[test]
6278fn src_line_9063() {
6279 let input = r#"<a h*#ref="hi">"#;
6280 let output = r#"<p>&lt;a h*#ref=&quot;hi&quot;&gt;</p>"#;
6281 run(input, output);
6282}
6283
6284#[test]
6285fn src_line_9072() {
6286 let input = r#"<a href="hi'> <a href=hi'>"#;
6287 let output = r#"<p>&lt;a href=&quot;hi'&gt; &lt;a href=hi'&gt;</p>"#;
6288 run(input, output);
6289}
6290
6291#[test]
6292fn src_line_9081() {
6293 let input = r#"< a><
6294foo><bar/ >
6295<foo bar=baz
6296bim!bop />"#;
6297 let output = r#"<p>&lt; a&gt;&lt;
6298foo&gt;&lt;bar/ &gt;
6299&lt;foo bar=baz
6300bim!bop /&gt;</p>"#;
6301 run(input, output);
6302}
6303
6304#[test]
6305fn src_line_9096() {
6306 let input = r#"<a href='bar'title=title>"#;
6307 let output = r#"<p>&lt;a href='bar'title=title&gt;</p>"#;
6308 run(input, output);
6309}
6310
6311#[test]
6312fn src_line_9105() {
6313 let input = r#"</a></foo >"#;
6314 let output = r#"<p></a></foo ></p>"#;
6315 run(input, output);
6316}
6317
6318#[test]
6319fn src_line_9114() {
6320 let input = r#"</a href="foo">"#;
6321 let output = r#"<p>&lt;/a href=&quot;foo&quot;&gt;</p>"#;
6322 run(input, output);
6323}
6324
6325#[test]
6326fn src_line_9123() {
6327 let input = r#"foo <!-- this is a
6328comment - with hyphen -->"#;
6329 let output = r#"<p>foo <!-- this is a
6330comment - with hyphen --></p>"#;
6331 run(input, output);
6332}
6333
6334#[test]
6335fn src_line_9132() {
6336 let input = r#"foo <!-- not a comment -- two hyphens -->"#;
6337 let output = r#"<p>foo &lt;!-- not a comment -- two hyphens --&gt;</p>"#;
6338 run(input, output);
6339}
6340
6341#[test]
6342fn src_line_9141() {
6343 let input = r#"foo <!--> foo -->
6344
6345foo <!-- foo--->"#;
6346 let output = r#"<p>foo &lt;!--&gt; foo --&gt;</p>
6347<p>foo &lt;!-- foo---&gt;</p>"#;
6348 run(input, output);
6349}
6350
6351#[test]
6352fn src_line_9153() {
6353 let input = r#"foo <?php echo $a; ?>"#;
6354 let output = r#"<p>foo <?php echo $a; ?></p>"#;
6355 run(input, output);
6356}
6357
6358#[test]
6359fn src_line_9162() {
6360 let input = r#"foo <!ELEMENT br EMPTY>"#;
6361 let output = r#"<p>foo <!ELEMENT br EMPTY></p>"#;
6362 run(input, output);
6363}
6364
6365#[test]
6366fn src_line_9171() {
6367 let input = r#"foo <![CDATA[>&<]]>"#;
6368 let output = r#"<p>foo <![CDATA[>&<]]></p>"#;
6369 run(input, output);
6370}
6371
6372#[test]
6373fn src_line_9181() {
6374 let input = r#"foo <a href="&ouml;">"#;
6375 let output = r#"<p>foo <a href="&ouml;"></p>"#;
6376 run(input, output);
6377}
6378
6379#[test]
6380fn src_line_9190() {
6381 let input = r#"foo <a href="\*">"#;
6382 let output = r#"<p>foo <a href="\*"></p>"#;
6383 run(input, output);
6384}
6385
6386#[test]
6387fn src_line_9197() {
6388 let input = r#"<a href="\"">"#;
6389 let output = r#"<p>&lt;a href=&quot;&quot;&quot;&gt;</p>"#;
6390 run(input, output);
6391}
6392
6393#[test]
6394fn src_line_9211() {
6395 let input = "foo \x20
6396baz";
6397 let output = r#"<p>foo<br />
6398baz</p>"#;
6399 run(input, output);
6400}
6401
6402#[test]
6403fn src_line_9223() {
6404 let input = r#"foo\
6405baz"#;
6406 let output = r#"<p>foo<br />
6407baz</p>"#;
6408 run(input, output);
6409}
6410
6411#[test]
6412fn src_line_9234() {
6413 let input = "foo \x20
6414baz";
6415 let output = r#"<p>foo<br />
6416baz</p>"#;
6417 run(input, output);
6418}
6419
6420#[test]
6421fn src_line_9245() {
6422 let input = "foo \x20
6423 bar";
6424 let output = r#"<p>foo<br />
6425bar</p>"#;
6426 run(input, output);
6427}
6428
6429#[test]
6430fn src_line_9254() {
6431 let input = r#"foo\
6432 bar"#;
6433 let output = r#"<p>foo<br />
6434bar</p>"#;
6435 run(input, output);
6436}
6437
6438#[test]
6439fn src_line_9266() {
6440 let input = "*foo \x20
6441bar*";
6442 let output = r#"<p><em>foo<br />
6443bar</em></p>"#;
6444 run(input, output);
6445}
6446
6447#[test]
6448fn src_line_9275() {
6449 let input = r#"*foo\
6450bar*"#;
6451 let output = r#"<p><em>foo<br />
6452bar</em></p>"#;
6453 run(input, output);
6454}
6455
6456#[test]
6457fn src_line_9286() {
6458 let input = "`code \x20
6459span`";
6460 let output = r#"<p><code>code span</code></p>"#;
6461 run(input, output);
6462}
6463
6464#[test]
6465fn src_line_9294() {
6466 let input = r#"`code\
6467span`"#;
6468 let output = r#"<p><code>code\ span</code></p>"#;
6469 run(input, output);
6470}
6471
6472#[test]
6473fn src_line_9304() {
6474 let input = "<a href=\"foo \x20
6475bar\">";
6476 let output = "<p><a href=\"foo \x20
6477bar\"></p>";
6478 run(input, output);
6479}
6480
6481#[test]
6482fn src_line_9313() {
6483 let input = r#"<a href="foo\
6484bar">"#;
6485 let output = r#"<p><a href="foo\
6486bar"></p>"#;
6487 run(input, output);
6488}
6489
6490#[test]
6491fn src_line_9326() {
6492 let input = r#"foo\"#;
6493 let output = r#"<p>foo\</p>"#;
6494 run(input, output);
6495}
6496
6497#[test]
6498fn src_line_9333() {
6499 let input = "foo \x20";
6500 let output = r#"<p>foo</p>"#;
6501 run(input, output);
6502}
6503
6504#[test]
6505fn src_line_9340() {
6506 let input = r#"### foo\"#;
6507 let output = r#"<h3>foo\</h3>"#;
6508 run(input, output);
6509}
6510
6511#[test]
6512fn src_line_9347() {
6513 let input = "### foo \x20";
6514 let output = r#"<h3>foo</h3>"#;
6515 run(input, output);
6516}
6517
6518#[test]
6519fn src_line_9362() {
6520 let input = r#"foo
6521baz"#;
6522 let output = r#"<p>foo
6523baz</p>"#;
6524 run(input, output);
6525}
6526
6527#[test]
6528fn src_line_9374() {
6529 let input = "foo\x20
6530 baz";
6531 let output = r#"<p>foo
6532baz</p>"#;
6533 run(input, output);
6534}
6535
6536#[test]
6537fn src_line_9394() {
6538 let input = r#"hello $.;'there"#;
6539 let output = r#"<p>hello $.;'there</p>"#;
6540 run(input, output);
6541}
6542
6543#[test]
6544fn src_line_9401() {
6545 let input = r#"Foo χρῆν"#;
6546 let output = r#"<p>Foo χρῆν</p>"#;
6547 run(input, output);
6548}
6549
6550#[test]
6551fn src_line_9410() {
6552 let input = r#"Multiple spaces"#;
6553 let output = r#"<p>Multiple spaces</p>"#;
6554 run(input, output);
6555}
6556// end of auto-generated module
6557}
crates/markdown-it/tests/extras.rs created+202
...@@ -0,0 +1,202 @@
1use once_cell::sync::Lazy;
2
3
4#[test]
5fn title_example() {
6 let parser = &mut markdown_it::MarkdownIt::new();
7 markdown_it::plugins::cmark::add(parser);
8
9 let ast = parser.parse("Hello **world**!");
10 let html = ast.render();
11
12 assert_eq!(html, "<p>Hello <strong>world</strong>!</p>\n");
13}
14
15#[test]
16fn lazy_singleton() {
17 static MD : Lazy<markdown_it::MarkdownIt> = Lazy::new(|| {
18 let mut parser = markdown_it::MarkdownIt::new();
19 markdown_it::plugins::cmark::add(&mut parser);
20 parser
21 });
22
23 let ast = MD.parse("Hello **world**!");
24 let html = ast.render();
25
26 assert_eq!(html, "<p>Hello <strong>world</strong>!</p>\n");
27}
28
29#[test]
30fn no_plugins() {
31 let md = &mut markdown_it::MarkdownIt::new();
32 let node = md.parse("hello\nworld");
33 let result = node.render();
34 assert_eq!(result, "hello\nworld\n");
35}
36
37#[test]
38fn no_max_indent() {
39 let md = &mut markdown_it::MarkdownIt::new();
40 markdown_it::plugins::cmark::block::paragraph::add(md);
41 markdown_it::plugins::cmark::block::list::add(md);
42 md.max_indent = i32::MAX;
43 let node = md.parse(" paragraph\n - item");
44 let result = node.render();
45 assert_eq!(result, "<p>paragraph</p>\n<ul>\n<li>item</li>\n</ul>\n");
46}
47
48
49/*#[test]
50fn no_block_parser() {
51 let md = &mut markdown_it::MarkdownIt::new();
52 markdown_it::plugins::cmark::add(md);
53 md.remove_rule::<markdown_it::parser::block::builtin::BlockParserRule>();
54 let node = md.parse("hello *world*");
55 let result = node.render();
56 assert_eq!(result, "hello <em>world</em>");
57}*/
58
59fn run(input: &str, output: &str) {
60 let output = if output.is_empty() { "".to_owned() } else { output.to_owned() + "\n" };
61 let md = &mut markdown_it::MarkdownIt::new();
62 markdown_it::plugins::cmark::add(md);
63 markdown_it::plugins::html::add(md);
64 markdown_it::plugins::extra::beautify_links::add(md);
65 let node = md.parse(&(input.to_owned() + "\n"));
66 node.walk(|node, _| assert!(node.srcmap.is_some()));
67 let result = node.render();
68 assert_eq!(result, output);
69}
70
71mod markdown_it_rs_extras {
72 use super::run;
73
74 #[test]
75 fn regression_test_img() {
76 // ! at end of line
77 run("Hello!", "<p>Hello!</p>");
78 }
79
80 #[test]
81 fn regression_list_markers() {
82 run("- foo\n- bar", "<ul>\n<li>foo</li>\n<li>bar</li>\n</ul>");
83 run("1. foo\n1. bar", "<ol>\n<li>foo</li>\n<li>bar</li>\n</ol>");
84 }
85
86 #[test]
87 fn tab_offset_in_lists() {
88 run(" > -\tfoo\n >\n > foo\n",
89r#"<blockquote>
90<ul>
91<li>
92<p>foo</p>
93<pre><code> foo
94</code></pre>
95</li>
96</ul>
97</blockquote>"#);
98 }
99
100 #[test]
101 fn null_char_replacement() {
102 run("&#0;", "<p>\u{FFFD}</p>");
103 run("\0", "<p>\u{FFFD}</p>");
104 }
105
106 #[test]
107 fn cr_only_newlines() {
108 run("foo\rbar", "<p>foo\nbar</p>");
109 run(" foo\r bar", "<pre><code>foo\nbar\n</code></pre>");
110 }
111
112 #[test]
113 fn cr_lf_newlines() {
114 run("foo\r\nbar", "<p>foo\nbar</p>");
115 run(" foo\r\n bar", "<pre><code>foo\nbar\n</code></pre>");
116 }
117
118 #[test]
119 fn beautify_links() {
120 run("<https://www.reddit.com/r/programming/comments/vxttiq/comment/ifyqsqt/?utm_source=reddit&utm_medium=web2x&context=3>",
121 "<p><a href=\"https://www.reddit.com/r/programming/comments/vxttiq/comment/ifyqsqt/?utm_source=reddit&amp;utm_medium=web2x&amp;context=3\">www.reddit.com/r/programming/comments/…/ifyqsqt/?…</a></p>");
122 }
123
124 #[test]
125 fn regression_test_newlines_with_images() {
126 run("There is a newline in this image ![here\nit is](https://github.com/executablebooks/)",
127 "<p>There is a newline in this image <img src=\"https://github.com/executablebooks/\" alt=\"here\nit is\"></p>");
128 }
129
130 #[test]
131 fn test_node_ext_propagation() {
132 use markdown_it::parser::block::{BlockRule, BlockState};
133 use markdown_it::parser::core::CoreRule;
134 use markdown_it::parser::extset::NodeExt;
135 use markdown_it::parser::inline::{InlineRule, InlineState};
136 use markdown_it::{MarkdownIt, Node};
137
138 #[derive(Debug, Default)]
139 struct NodeErrors(Vec<&'static str>);
140 impl NodeExt for NodeErrors {}
141
142 struct MyInlineRule;
143 impl InlineRule for MyInlineRule {
144 const MARKER: char = '@';
145
146 fn run(state: &mut InlineState) -> Option<(Node, usize)> {
147 let err = state.node.ext.get_or_insert_default::<NodeErrors>();
148 err.0.push("inline");
149 None
150 }
151 }
152
153 struct MyBlockRule;
154 impl BlockRule for MyBlockRule {
155 fn run(state: &mut BlockState) -> Option<(Node, usize)> {
156 let err = state.node.ext.get_or_insert_default::<NodeErrors>();
157 err.0.push("block");
158 None
159 }
160 }
161
162 struct MyCoreRule;
163 impl CoreRule for MyCoreRule {
164 fn run(root: &mut Node, _md: &MarkdownIt) {
165 let err = root.ext.get_or_insert_default::<NodeErrors>();
166 err.0.push("core");
167 }
168 }
169
170 let md = &mut markdown_it::MarkdownIt::new();
171 markdown_it::plugins::cmark::add(md);
172
173 md.inline.add_rule::<MyInlineRule>();
174 md.block.add_rule::<MyBlockRule>();
175 md.add_rule::<MyCoreRule>().after_all();
176
177 let text1 = r#"*hello @world*"#;
178 let ast = md.parse(text1);
179 let mut collected: Vec<&str> = vec![];
180
181 ast.walk_post(|node, _| {
182 if let Some(errors) = node.ext.get::<NodeErrors>() {
183 collected.extend(errors.0.iter());
184 }
185 });
186
187 assert_eq!(
188 collected,
189 vec!["inline", "block", "core"],
190 );
191 }
192}
193
194mod examples {
195 include!("../examples/ferris/main.rs");
196
197 #[test]
198 fn test_examples() {
199 main();
200 }
201}
202
crates/markdown-it/tests/fixtures/README.md created+28
...@@ -0,0 +1,28 @@
1This file generates tests for markdown-it rust library. It gets one argument
2(file name) and edits that file in-place, replacing special markers inside
3with tests generated from fixtures.
4
5Run it like this from tests/ folder:
6```sh
7for I in *.rs ; do deno run --allow-read --allow-write ./fixtures/testgen.js $I ; done
8```
9
10As an example, it replaces this:
11```rs
12///////////////////////////////////////////////////////////////////////////
13// TESTGEN: fixtures/commonmark/spec.txt
14... any content here ...
15///////////////////////////////////////////////////////////////////////////
16```
17
18With approximately this:
19```rs
20///////////////////////////////////////////////////////////////////////////
21// TESTGEN: fixtures/commonmark/spec.txt
22#[test]
23fn line_123() {
24 run("*foo*", "<em>foo</em>");
25}
26... more tests here ...
27///////////////////////////////////////////////////////////////////////////
28```
crates/markdown-it/tests/fixtures/commonmark/bad.txt
crates/markdown-it/tests/fixtures/commonmark/good.txt created+7834
...@@ -0,0 +1,7834 @@
1~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2src line: 355
3
4.
5 foo baz bim
6.
7<pre><code>foo baz bim
8</code></pre>
9.
10
11~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
12src line: 362
13
14.
15 foo baz bim
16.
17<pre><code>foo baz bim
18</code></pre>
19.
20
21~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
22src line: 369
23
24.
25 a a
26 ὐ a
27.
28<pre><code>a a
29ὐ a
30</code></pre>
31.
32
33~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
34src line: 382
35
36.
37 - foo
38
39 bar
40.
41<ul>
42<li>
43<p>foo</p>
44<p>bar</p>
45</li>
46</ul>
47.
48
49~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
50src line: 395
51
52.
53- foo
54
55 bar
56.
57<ul>
58<li>
59<p>foo</p>
60<pre><code> bar
61</code></pre>
62</li>
63</ul>
64.
65
66~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
67src line: 418
68
69.
70> foo
71.
72<blockquote>
73<pre><code> foo
74</code></pre>
75</blockquote>
76.
77
78~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
79src line: 427
80
81.
82- foo
83.
84<ul>
85<li>
86<pre><code> foo
87</code></pre>
88</li>
89</ul>
90.
91
92~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
93src line: 439
94
95.
96 foo
97 bar
98.
99<pre><code>foo
100bar
101</code></pre>
102.
103
104~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
105src line: 448
106
107.
108 - foo
109 - bar
110 - baz
111.
112<ul>
113<li>foo
114<ul>
115<li>bar
116<ul>
117<li>baz</li>
118</ul>
119</li>
120</ul>
121</li>
122</ul>
123.
124
125~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
126src line: 466
127
128.
129# Foo
130.
131<h1>Foo</h1>
132.
133
134~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
135src line: 472
136
137.
138* * *
139.
140<hr />
141.
142
143~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
144src line: 489
145
146.
147\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~
148.
149<p>!&quot;#$%&amp;'()*+,-./:;&lt;=&gt;?@[\]^_`{|}~</p>
150.
151
152~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
153src line: 499
154
155.
156\ \A\a\ \3\φ\«
157.
158<p>\ \A\a\ \3\φ\«</p>
159.
160
161~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
162src line: 509
163
164.
165\*not emphasized*
166\<br/> not a tag
167\[not a link](/foo)
168\`not code`
1691\. not a list
170\* not a list
171\# not a heading
172\[foo]: /url "not a reference"
173\&ouml; not a character entity
174.
175<p>*not emphasized*
176&lt;br/&gt; not a tag
177[not a link](/foo)
178`not code`
1791. not a list
180* not a list
181# not a heading
182[foo]: /url &quot;not a reference&quot;
183&amp;ouml; not a character entity</p>
184.
185
186~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
187src line: 534
188
189.
190\\*emphasis*
191.
192<p>\<em>emphasis</em></p>
193.
194
195~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
196src line: 543
197
198.
199foo\
200bar
201.
202<p>foo<br />
203bar</p>
204.
205
206~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
207src line: 555
208
209.
210`` \[\` ``
211.
212<p><code>\[\`</code></p>
213.
214
215~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
216src line: 562
217
218.
219 \[\]
220.
221<pre><code>\[\]
222</code></pre>
223.
224
225~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
226src line: 570
227
228.
229~~~
230\[\]
231~~~
232.
233<pre><code>\[\]
234</code></pre>
235.
236
237~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
238src line: 580
239
240.
241<http://example.com?find=\*>
242.
243<p><a href="http://example.com?find=%5C*">http://example.com?find=\*</a></p>
244.
245
246~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
247src line: 587
248
249.
250<a href="/bar\/)">
251.
252<a href="/bar\/)">
253.
254
255~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
256src line: 597
257
258.
259[foo](/bar\* "ti\*tle")
260.
261<p><a href="/bar*" title="ti*tle">foo</a></p>
262.
263
264~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
265src line: 604
266
267.
268[foo]
269
270[foo]: /bar\* "ti\*tle"
271.
272<p><a href="/bar*" title="ti*tle">foo</a></p>
273.
274
275~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
276src line: 613
277
278.
279``` foo\+bar
280foo
281```
282.
283<pre><code class="language-foo+bar">foo
284</code></pre>
285.
286
287~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
288src line: 649
289
290.
291&nbsp; &amp; &copy; &AElig; &Dcaron;
292&frac34; &HilbertSpace; &DifferentialD;
293&ClockwiseContourIntegral; &ngE;
294.
295<p>  &amp; © Æ Ď
296¾ ℋ ⅆ
297∲ ≧̸</p>
298.
299
300~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
301src line: 668
302
303.
304&#35; &#1234; &#992; &#0;
305.
306<p># Ӓ Ϡ �</p>
307.
308
309~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
310src line: 681
311
312.
313&#X22; &#XD06; &#xcab;
314.
315<p>&quot; ആ ಫ</p>
316.
317
318~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
319src line: 690
320
321.
322&nbsp &x; &#; &#x;
323&#87654321;
324&#abcdef0;
325&ThisIsNotDefined; &hi?;
326.
327<p>&amp;nbsp &amp;x; &amp;#; &amp;#x;
328&amp;#87654321;
329&amp;#abcdef0;
330&amp;ThisIsNotDefined; &amp;hi?;</p>
331.
332
333~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
334src line: 707
335
336.
337&copy
338.
339<p>&amp;copy</p>
340.
341
342~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
343src line: 717
344
345.
346&MadeUpEntity;
347.
348<p>&amp;MadeUpEntity;</p>
349.
350
351~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
352src line: 728
353
354.
355<a href="&ouml;&ouml;.html">
356.
357<a href="&ouml;&ouml;.html">
358.
359
360~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
361src line: 735
362
363.
364[foo](/f&ouml;&ouml; "f&ouml;&ouml;")
365.
366<p><a href="/f%C3%B6%C3%B6" title="föö">foo</a></p>
367.
368
369~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
370src line: 742
371
372.
373[foo]
374
375[foo]: /f&ouml;&ouml; "f&ouml;&ouml;"
376.
377<p><a href="/f%C3%B6%C3%B6" title="föö">foo</a></p>
378.
379
380~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
381src line: 751
382
383.
384``` f&ouml;&ouml;
385foo
386```
387.
388<pre><code class="language-föö">foo
389</code></pre>
390.
391
392~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
393src line: 764
394
395.
396`f&ouml;&ouml;`
397.
398<p><code>f&amp;ouml;&amp;ouml;</code></p>
399.
400
401~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
402src line: 771
403
404.
405 f&ouml;f&ouml;
406.
407<pre><code>f&amp;ouml;f&amp;ouml;
408</code></pre>
409.
410
411~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
412src line: 783
413
414.
415&#42;foo&#42;
416*foo*
417.
418<p>*foo*
419<em>foo</em></p>
420.
421
422~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
423src line: 791
424
425.
426&#42; foo
427
428* foo
429.
430<p>* foo</p>
431<ul>
432<li>foo</li>
433</ul>
434.
435
436~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
437src line: 802
438
439.
440foo&#10;&#10;bar
441.
442<p>foo
443
444bar</p>
445.
446
447~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
448src line: 810
449
450.
451&#9;foo
452.
453<p> foo</p>
454.
455
456~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
457src line: 817
458
459.
460[a](url &quot;tit&quot;)
461.
462<p>[a](url &quot;tit&quot;)</p>
463.
464
465~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
466src line: 840
467
468.
469- `one
470- two`
471.
472<ul>
473<li>`one</li>
474<li>two`</li>
475</ul>
476.
477
478~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
479src line: 879
480
481.
482***
483---
484___
485.
486<hr />
487<hr />
488<hr />
489.
490
491~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
492src line: 892
493
494.
495+++
496.
497<p>+++</p>
498.
499
500~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
501src line: 899
502
503.
504===
505.
506<p>===</p>
507.
508
509~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
510src line: 908
511
512.
513--
514**
515__
516.
517<p>--
518**
519__</p>
520.
521
522~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
523src line: 921
524
525.
526 ***
527 ***
528 ***
529.
530<hr />
531<hr />
532<hr />
533.
534
535~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
536src line: 934
537
538.
539 ***
540.
541<pre><code>***
542</code></pre>
543.
544
545~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
546src line: 942
547
548.
549Foo
550 ***
551.
552<p>Foo
553***</p>
554.
555
556~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
557src line: 953
558
559.
560_____________________________________
561.
562<hr />
563.
564
565~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
566src line: 962
567
568.
569 - - -
570.
571<hr />
572.
573
574~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
575src line: 969
576
577.
578 ** * ** * ** * **
579.
580<hr />
581.
582
583~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
584src line: 976
585
586.
587- - - -
588.
589<hr />
590.
591
592~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
593src line: 985
594
595.
596- - - -
597.
598<hr />
599.
600
601~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
602src line: 994
603
604.
605_ _ _ _ a
606
607a------
608
609---a---
610.
611<p>_ _ _ _ a</p>
612<p>a------</p>
613<p>---a---</p>
614.
615
616~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
617src line: 1010
618
619.
620 *-*
621.
622<p><em>-</em></p>
623.
624
625~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
626src line: 1019
627
628.
629- foo
630***
631- bar
632.
633<ul>
634<li>foo</li>
635</ul>
636<hr />
637<ul>
638<li>bar</li>
639</ul>
640.
641
642~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
643src line: 1036
644
645.
646Foo
647***
648bar
649.
650<p>Foo</p>
651<hr />
652<p>bar</p>
653.
654
655~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
656src line: 1053
657
658.
659Foo
660---
661bar
662.
663<h2>Foo</h2>
664<p>bar</p>
665.
666
667~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
668src line: 1066
669
670.
671* Foo
672* * *
673* Bar
674.
675<ul>
676<li>Foo</li>
677</ul>
678<hr />
679<ul>
680<li>Bar</li>
681</ul>
682.
683
684~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
685src line: 1083
686
687.
688- Foo
689- * * *
690.
691<ul>
692<li>Foo</li>
693<li>
694<hr />
695</li>
696</ul>
697.
698
699~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
700src line: 1112
701
702.
703# foo
704## foo
705### foo
706#### foo
707##### foo
708###### foo
709.
710<h1>foo</h1>
711<h2>foo</h2>
712<h3>foo</h3>
713<h4>foo</h4>
714<h5>foo</h5>
715<h6>foo</h6>
716.
717
718~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
719src line: 1131
720
721.
722####### foo
723.
724<p>####### foo</p>
725.
726
727~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
728src line: 1146
729
730.
731#5 bolt
732
733#hashtag
734.
735<p>#5 bolt</p>
736<p>#hashtag</p>
737.
738
739~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
740src line: 1158
741
742.
743\## foo
744.
745<p>## foo</p>
746.
747
748~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
749src line: 1167
750
751.
752# foo *bar* \*baz\*
753.
754<h1>foo <em>bar</em> *baz*</h1>
755.
756
757~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
758src line: 1176
759
760.
761# foo
762.
763<h1>foo</h1>
764.
765
766~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
767src line: 1185
768
769.
770 ### foo
771 ## foo
772 # foo
773.
774<h3>foo</h3>
775<h2>foo</h2>
776<h1>foo</h1>
777.
778
779~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
780src line: 1198
781
782.
783 # foo
784.
785<pre><code># foo
786</code></pre>
787.
788
789~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
790src line: 1206
791
792.
793foo
794 # bar
795.
796<p>foo
797# bar</p>
798.
799
800~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
801src line: 1217
802
803.
804## foo ##
805 ### bar ###
806.
807<h2>foo</h2>
808<h3>bar</h3>
809.
810
811~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
812src line: 1228
813
814.
815# foo ##################################
816##### foo ##
817.
818<h1>foo</h1>
819<h5>foo</h5>
820.
821
822~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
823src line: 1239
824
825.
826### foo ###
827.
828<h3>foo</h3>
829.
830
831~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
832src line: 1250
833
834.
835### foo ### b
836.
837<h3>foo ### b</h3>
838.
839
840~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
841src line: 1259
842
843.
844# foo#
845.
846<h1>foo#</h1>
847.
848
849~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
850src line: 1269
851
852.
853### foo \###
854## foo #\##
855# foo \#
856.
857<h3>foo ###</h3>
858<h2>foo ###</h2>
859<h1>foo #</h1>
860.
861
862~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
863src line: 1283
864
865.
866****
867## foo
868****
869.
870<hr />
871<h2>foo</h2>
872<hr />
873.
874
875~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
876src line: 1294
877
878.
879Foo bar
880# baz
881Bar foo
882.
883<p>Foo bar</p>
884<h1>baz</h1>
885<p>Bar foo</p>
886.
887
888~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
889src line: 1307
890
891.
892##
893#
894### ###
895.
896<h2></h2>
897<h1></h1>
898<h3></h3>
899.
900
901~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
902src line: 1350
903
904.
905Foo *bar*
906=========
907
908Foo *bar*
909---------
910.
911<h1>Foo <em>bar</em></h1>
912<h2>Foo <em>bar</em></h2>
913.
914
915~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
916src line: 1364
917
918.
919Foo *bar
920baz*
921====
922.
923<h1>Foo <em>bar
924baz</em></h1>
925.
926
927~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
928src line: 1378
929
930.
931 Foo *bar
932baz*
933====
934.
935<h1>Foo <em>bar
936baz</em></h1>
937.
938
939~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
940src line: 1390
941
942.
943Foo
944-------------------------
945
946Foo
947=
948.
949<h2>Foo</h2>
950<h1>Foo</h1>
951.
952
953~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
954src line: 1405
955
956.
957 Foo
958---
959
960 Foo
961-----
962
963 Foo
964 ===
965.
966<h2>Foo</h2>
967<h2>Foo</h2>
968<h1>Foo</h1>
969.
970
971~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
972src line: 1423
973
974.
975 Foo
976 ---
977
978 Foo
979---
980.
981<pre><code>Foo
982---
983
984Foo
985</code></pre>
986<hr />
987.
988
989~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
990src line: 1442
991
992.
993Foo
994 ----
995.
996<h2>Foo</h2>
997.
998
999~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1000src line: 1452
1001
1002.
1003Foo
1004 ---
1005.
1006<p>Foo
1007---</p>
1008.
1009
1010~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1011src line: 1463
1012
1013.
1014Foo
1015= =
1016
1017Foo
1018--- -
1019.
1020<p>Foo
1021= =</p>
1022<p>Foo</p>
1023<hr />
1024.
1025
1026~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1027src line: 1479
1028
1029.
1030Foo
1031-----
1032.
1033<h2>Foo</h2>
1034.
1035
1036~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1037src line: 1489
1038
1039.
1040Foo\
1041----
1042.
1043<h2>Foo\</h2>
1044.
1045
1046~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1047src line: 1500
1048
1049.
1050`Foo
1051----
1052`
1053
1054<a title="a lot
1055---
1056of dashes"/>
1057.
1058<h2>`Foo</h2>
1059<p>`</p>
1060<h2>&lt;a title=&quot;a lot</h2>
1061<p>of dashes&quot;/&gt;</p>
1062.
1063
1064~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1065src line: 1519
1066
1067.
1068> Foo
1069---
1070.
1071<blockquote>
1072<p>Foo</p>
1073</blockquote>
1074<hr />
1075.
1076
1077~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1078src line: 1530
1079
1080.
1081> foo
1082bar
1083===
1084.
1085<blockquote>
1086<p>foo
1087bar
1088===</p>
1089</blockquote>
1090.
1091
1092~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1093src line: 1543
1094
1095.
1096- Foo
1097---
1098.
1099<ul>
1100<li>Foo</li>
1101</ul>
1102<hr />
1103.
1104
1105~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1106src line: 1558
1107
1108.
1109Foo
1110Bar
1111---
1112.
1113<h2>Foo
1114Bar</h2>
1115.
1116
1117~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1118src line: 1571
1119
1120.
1121---
1122Foo
1123---
1124Bar
1125---
1126Baz
1127.
1128<hr />
1129<h2>Foo</h2>
1130<h2>Bar</h2>
1131<p>Baz</p>
1132.
1133
1134~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1135src line: 1588
1136
1137.
1138
1139====
1140.
1141<p>====</p>
1142.
1143
1144~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1145src line: 1600
1146
1147.
1148---
1149---
1150.
1151<hr />
1152<hr />
1153.
1154
1155~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1156src line: 1609
1157
1158.
1159- foo
1160-----
1161.
1162<ul>
1163<li>foo</li>
1164</ul>
1165<hr />
1166.
1167
1168~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1169src line: 1620
1170
1171.
1172 foo
1173---
1174.
1175<pre><code>foo
1176</code></pre>
1177<hr />
1178.
1179
1180~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1181src line: 1630
1182
1183.
1184> foo
1185-----
1186.
1187<blockquote>
1188<p>foo</p>
1189</blockquote>
1190<hr />
1191.
1192
1193~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1194src line: 1644
1195
1196.
1197\> foo
1198------
1199.
1200<h2>&gt; foo</h2>
1201.
1202
1203~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1204src line: 1675
1205
1206.
1207Foo
1208
1209bar
1210---
1211baz
1212.
1213<p>Foo</p>
1214<h2>bar</h2>
1215<p>baz</p>
1216.
1217
1218~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1219src line: 1691
1220
1221.
1222Foo
1223bar
1224
1225---
1226
1227baz
1228.
1229<p>Foo
1230bar</p>
1231<hr />
1232<p>baz</p>
1233.
1234
1235~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1236src line: 1709
1237
1238.
1239Foo
1240bar
1241* * *
1242baz
1243.
1244<p>Foo
1245bar</p>
1246<hr />
1247<p>baz</p>
1248.
1249
1250~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1251src line: 1724
1252
1253.
1254Foo
1255bar
1256\---
1257baz
1258.
1259<p>Foo
1260bar
1261---
1262baz</p>
1263.
1264
1265~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1266src line: 1752
1267
1268.
1269 a simple
1270 indented code block
1271.
1272<pre><code>a simple
1273 indented code block
1274</code></pre>
1275.
1276
1277~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1278src line: 1766
1279
1280.
1281 - foo
1282
1283 bar
1284.
1285<ul>
1286<li>
1287<p>foo</p>
1288<p>bar</p>
1289</li>
1290</ul>
1291.
1292
1293~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1294src line: 1780
1295
1296.
12971. foo
1298
1299 - bar
1300.
1301<ol>
1302<li>
1303<p>foo</p>
1304<ul>
1305<li>bar</li>
1306</ul>
1307</li>
1308</ol>
1309.
1310
1311~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1312src line: 1800
1313
1314.
1315 <a/>
1316 *hi*
1317
1318 - one
1319.
1320<pre><code>&lt;a/&gt;
1321*hi*
1322
1323- one
1324</code></pre>
1325.
1326
1327~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1328src line: 1816
1329
1330.
1331 chunk1
1332
1333 chunk2
1334
1335
1336
1337 chunk3
1338.
1339<pre><code>chunk1
1340
1341chunk2
1342
1343
1344
1345chunk3
1346</code></pre>
1347.
1348
1349~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1350src line: 1839
1351
1352.
1353 chunk1
1354
1355 chunk2
1356.
1357<pre><code>chunk1
1358
1359 chunk2
1360</code></pre>
1361.
1362
1363~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1364src line: 1854
1365
1366.
1367Foo
1368 bar
1369
1370.
1371<p>Foo
1372bar</p>
1373.
1374
1375~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1376src line: 1868
1377
1378.
1379 foo
1380bar
1381.
1382<pre><code>foo
1383</code></pre>
1384<p>bar</p>
1385.
1386
1387~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1388src line: 1881
1389
1390.
1391# Heading
1392 foo
1393Heading
1394------
1395 foo
1396----
1397.
1398<h1>Heading</h1>
1399<pre><code>foo
1400</code></pre>
1401<h2>Heading</h2>
1402<pre><code>foo
1403</code></pre>
1404<hr />
1405.
1406
1407~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1408src line: 1901
1409
1410.
1411 foo
1412 bar
1413.
1414<pre><code> foo
1415bar
1416</code></pre>
1417.
1418
1419~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1420src line: 1914
1421
1422.
1423
1424
1425 foo
1426
1427
1428.
1429<pre><code>foo
1430</code></pre>
1431.
1432
1433~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1434src line: 1928
1435
1436.
1437 foo
1438.
1439<pre><code>foo
1440</code></pre>
1441.
1442
1443~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1444src line: 1983
1445
1446.
1447```
1448<
1449 >
1450```
1451.
1452<pre><code>&lt;
1453 &gt;
1454</code></pre>
1455.
1456
1457~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1458src line: 1997
1459
1460.
1461~~~
1462<
1463 >
1464~~~
1465.
1466<pre><code>&lt;
1467 &gt;
1468</code></pre>
1469.
1470
1471~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1472src line: 2010
1473
1474.
1475``
1476foo
1477``
1478.
1479<p><code>foo</code></p>
1480.
1481
1482~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1483src line: 2021
1484
1485.
1486```
1487aaa
1488~~~
1489```
1490.
1491<pre><code>aaa
1492~~~
1493</code></pre>
1494.
1495
1496~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1497src line: 2033
1498
1499.
1500~~~
1501aaa
1502```
1503~~~
1504.
1505<pre><code>aaa
1506```
1507</code></pre>
1508.
1509
1510~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1511src line: 2047
1512
1513.
1514````
1515aaa
1516```
1517``````
1518.
1519<pre><code>aaa
1520```
1521</code></pre>
1522.
1523
1524~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1525src line: 2059
1526
1527.
1528~~~~
1529aaa
1530~~~
1531~~~~
1532.
1533<pre><code>aaa
1534~~~
1535</code></pre>
1536.
1537
1538~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1539src line: 2074
1540
1541.
1542```
1543.
1544<pre><code></code></pre>
1545.
1546
1547~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1548src line: 2081
1549
1550.
1551`````
1552
1553```
1554aaa
1555.
1556<pre><code>
1557```
1558aaa
1559</code></pre>
1560.
1561
1562~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1563src line: 2094
1564
1565.
1566> ```
1567> aaa
1568
1569bbb
1570.
1571<blockquote>
1572<pre><code>aaa
1573</code></pre>
1574</blockquote>
1575<p>bbb</p>
1576.
1577
1578~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1579src line: 2110
1580
1581.
1582```
1583
1584
1585```
1586.
1587<pre><code>
1588
1589</code></pre>
1590.
1591
1592~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1593src line: 2124
1594
1595.
1596```
1597```
1598.
1599<pre><code></code></pre>
1600.
1601
1602~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1603src line: 2136
1604
1605.
1606 ```
1607 aaa
1608aaa
1609```
1610.
1611<pre><code>aaa
1612aaa
1613</code></pre>
1614.
1615
1616~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1617src line: 2148
1618
1619.
1620 ```
1621aaa
1622 aaa
1623aaa
1624 ```
1625.
1626<pre><code>aaa
1627aaa
1628aaa
1629</code></pre>
1630.
1631
1632~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1633src line: 2162
1634
1635.
1636 ```
1637 aaa
1638 aaa
1639 aaa
1640 ```
1641.
1642<pre><code>aaa
1643 aaa
1644aaa
1645</code></pre>
1646.
1647
1648~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1649src line: 2178
1650
1651.
1652 ```
1653 aaa
1654 ```
1655.
1656<pre><code>```
1657aaa
1658```
1659</code></pre>
1660.
1661
1662~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1663src line: 2193
1664
1665.
1666```
1667aaa
1668 ```
1669.
1670<pre><code>aaa
1671</code></pre>
1672.
1673
1674~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1675src line: 2203
1676
1677.
1678 ```
1679aaa
1680 ```
1681.
1682<pre><code>aaa
1683</code></pre>
1684.
1685
1686~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1687src line: 2215
1688
1689.
1690```
1691aaa
1692 ```
1693.
1694<pre><code>aaa
1695 ```
1696</code></pre>
1697.
1698
1699~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1700src line: 2229
1701
1702.
1703``` ```
1704aaa
1705.
1706<p><code> </code>
1707aaa</p>
1708.
1709
1710~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1711src line: 2238
1712
1713.
1714~~~~~~
1715aaa
1716~~~ ~~
1717.
1718<pre><code>aaa
1719~~~ ~~
1720</code></pre>
1721.
1722
1723~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1724src line: 2252
1725
1726.
1727foo
1728```
1729bar
1730```
1731baz
1732.
1733<p>foo</p>
1734<pre><code>bar
1735</code></pre>
1736<p>baz</p>
1737.
1738
1739~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1740src line: 2269
1741
1742.
1743foo
1744---
1745~~~
1746bar
1747~~~
1748# baz
1749.
1750<h2>foo</h2>
1751<pre><code>bar
1752</code></pre>
1753<h1>baz</h1>
1754.
1755
1756~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1757src line: 2291
1758
1759.
1760```ruby
1761def foo(x)
1762 return 3
1763end
1764```
1765.
1766<pre><code class="language-ruby">def foo(x)
1767 return 3
1768end
1769</code></pre>
1770.
1771
1772~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1773src line: 2305
1774
1775.
1776~~~~ ruby startline=3 $%@#$
1777def foo(x)
1778 return 3
1779end
1780~~~~~~~
1781.
1782<pre><code class="language-ruby">def foo(x)
1783 return 3
1784end
1785</code></pre>
1786.
1787
1788~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1789src line: 2319
1790
1791.
1792````;
1793````
1794.
1795<pre><code class="language-;"></code></pre>
1796.
1797
1798~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1799src line: 2329
1800
1801.
1802``` aa ```
1803foo
1804.
1805<p><code>aa</code>
1806foo</p>
1807.
1808
1809~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1810src line: 2340
1811
1812.
1813~~~ aa ``` ~~~
1814foo
1815~~~
1816.
1817<pre><code class="language-aa">foo
1818</code></pre>
1819.
1820
1821~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1822src line: 2352
1823
1824.
1825```
1826``` aaa
1827```
1828.
1829<pre><code>``` aaa
1830</code></pre>
1831.
1832
1833~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1834src line: 2431
1835
1836.
1837<table><tr><td>
1838<pre>
1839**Hello**,
1840
1841_world_.
1842</pre>
1843</td></tr></table>
1844.
1845<table><tr><td>
1846<pre>
1847**Hello**,
1848<p><em>world</em>.
1849</pre></p>
1850</td></tr></table>
1851.
1852
1853~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1854src line: 2460
1855
1856.
1857<table>
1858 <tr>
1859 <td>
1860 hi
1861 </td>
1862 </tr>
1863</table>
1864
1865okay.
1866.
1867<table>
1868 <tr>
1869 <td>
1870 hi
1871 </td>
1872 </tr>
1873</table>
1874<p>okay.</p>
1875.
1876
1877~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1878src line: 2482
1879
1880.
1881 <div>
1882 *hello*
1883 <foo><a>
1884.
1885 <div>
1886 *hello*
1887 <foo><a>
1888.
1889
1890~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1891src line: 2495
1892
1893.
1894</div>
1895*foo*
1896.
1897</div>
1898*foo*
1899.
1900
1901~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1902src line: 2506
1903
1904.
1905<DIV CLASS="foo">
1906
1907*Markdown*
1908
1909</DIV>
1910.
1911<DIV CLASS="foo">
1912<p><em>Markdown</em></p>
1913</DIV>
1914.
1915
1916~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1917src line: 2522
1918
1919.
1920<div id="foo"
1921 class="bar">
1922</div>
1923.
1924<div id="foo"
1925 class="bar">
1926</div>
1927.
1928
1929~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1930src line: 2533
1931
1932.
1933<div id="foo" class="bar
1934 baz">
1935</div>
1936.
1937<div id="foo" class="bar
1938 baz">
1939</div>
1940.
1941
1942~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1943src line: 2545
1944
1945.
1946<div>
1947*foo*
1948
1949*bar*
1950.
1951<div>
1952*foo*
1953<p><em>bar</em></p>
1954.
1955
1956~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1957src line: 2561
1958
1959.
1960<div id="foo"
1961*hi*
1962.
1963<div id="foo"
1964*hi*
1965.
1966
1967~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1968src line: 2570
1969
1970.
1971<div class
1972foo
1973.
1974<div class
1975foo
1976.
1977
1978~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1979src line: 2582
1980
1981.
1982<div *???-&&&-<---
1983*foo*
1984.
1985<div *???-&&&-<---
1986*foo*
1987.
1988
1989~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1990src line: 2594
1991
1992.
1993<div><a href="bar">*foo*</a></div>
1994.
1995<div><a href="bar">*foo*</a></div>
1996.
1997
1998~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1999src line: 2601
2000
2001.
2002<table><tr><td>
2003foo
2004</td></tr></table>
2005.
2006<table><tr><td>
2007foo
2008</td></tr></table>
2009.
2010
2011~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2012src line: 2618
2013
2014.
2015<div></div>
2016``` c
2017int x = 33;
2018```
2019.
2020<div></div>
2021``` c
2022int x = 33;
2023```
2024.
2025
2026~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2027src line: 2635
2028
2029.
2030<a href="foo">
2031*bar*
2032</a>
2033.
2034<a href="foo">
2035*bar*
2036</a>
2037.
2038
2039~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2040src line: 2648
2041
2042.
2043<Warning>
2044*bar*
2045</Warning>
2046.
2047<Warning>
2048*bar*
2049</Warning>
2050.
2051
2052~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2053src line: 2659
2054
2055.
2056<i class="foo">
2057*bar*
2058</i>
2059.
2060<i class="foo">
2061*bar*
2062</i>
2063.
2064
2065~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2066src line: 2670
2067
2068.
2069</ins>
2070*bar*
2071.
2072</ins>
2073*bar*
2074.
2075
2076~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2077src line: 2685
2078
2079.
2080<del>
2081*foo*
2082</del>
2083.
2084<del>
2085*foo*
2086</del>
2087.
2088
2089~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2090src line: 2700
2091
2092.
2093<del>
2094
2095*foo*
2096
2097</del>
2098.
2099<del>
2100<p><em>foo</em></p>
2101</del>
2102.
2103
2104~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2105src line: 2718
2106
2107.
2108<del>*foo*</del>
2109.
2110<p><del><em>foo</em></del></p>
2111.
2112
2113~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2114src line: 2734
2115
2116.
2117<pre language="haskell"><code>
2118import Text.HTML.TagSoup
2119
2120main :: IO ()
2121main = print $ parseTags tags
2122</code></pre>
2123okay
2124.
2125<pre language="haskell"><code>
2126import Text.HTML.TagSoup
2127
2128main :: IO ()
2129main = print $ parseTags tags
2130</code></pre>
2131<p>okay</p>
2132.
2133
2134~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2135src line: 2755
2136
2137.
2138<script type="text/javascript">
2139// JavaScript example
2140
2141document.getElementById("demo").innerHTML = "Hello JavaScript!";
2142</script>
2143okay
2144.
2145<script type="text/javascript">
2146// JavaScript example
2147
2148document.getElementById("demo").innerHTML = "Hello JavaScript!";
2149</script>
2150<p>okay</p>
2151.
2152
2153~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2154src line: 2774
2155
2156.
2157<textarea>
2158
2159*foo*
2160
2161_bar_
2162
2163</textarea>
2164.
2165<textarea>
2166
2167*foo*
2168
2169_bar_
2170
2171</textarea>
2172.
2173
2174~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2175src line: 2794
2176
2177.
2178<style
2179 type="text/css">
2180h1 {color:red;}
2181
2182p {color:blue;}
2183</style>
2184okay
2185.
2186<style
2187 type="text/css">
2188h1 {color:red;}
2189
2190p {color:blue;}
2191</style>
2192<p>okay</p>
2193.
2194
2195~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2196src line: 2817
2197
2198.
2199<style
2200 type="text/css">
2201
2202foo
2203.
2204<style
2205 type="text/css">
2206
2207foo
2208.
2209
2210~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2211src line: 2830
2212
2213.
2214> <div>
2215> foo
2216
2217bar
2218.
2219<blockquote>
2220<div>
2221foo
2222</blockquote>
2223<p>bar</p>
2224.
2225
2226~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2227src line: 2844
2228
2229.
2230- <div>
2231- foo
2232.
2233<ul>
2234<li>
2235<div>
2236</li>
2237<li>foo</li>
2238</ul>
2239.
2240
2241~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2242src line: 2859
2243
2244.
2245<style>p{color:red;}</style>
2246*foo*
2247.
2248<style>p{color:red;}</style>
2249<p><em>foo</em></p>
2250.
2251
2252~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2253src line: 2868
2254
2255.
2256<!-- foo -->*bar*
2257*baz*
2258.
2259<!-- foo -->*bar*
2260<p><em>baz</em></p>
2261.
2262
2263~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2264src line: 2880
2265
2266.
2267<script>
2268foo
2269</script>1. *bar*
2270.
2271<script>
2272foo
2273</script>1. *bar*
2274.
2275
2276~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2277src line: 2893
2278
2279.
2280<!-- Foo
2281
2282bar
2283 baz -->
2284okay
2285.
2286<!-- Foo
2287
2288bar
2289 baz -->
2290<p>okay</p>
2291.
2292
2293~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2294src line: 2911
2295
2296.
2297<?php
2298
2299 echo '>';
2300
2301?>
2302okay
2303.
2304<?php
2305
2306 echo '>';
2307
2308?>
2309<p>okay</p>
2310.
2311
2312~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2313src line: 2930
2314
2315.
2316<!DOCTYPE html>
2317.
2318<!DOCTYPE html>
2319.
2320
2321~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2322src line: 2939
2323
2324.
2325<![CDATA[
2326function matchwo(a,b)
2327{
2328 if (a < b && a < 0) then {
2329 return 1;
2330
2331 } else {
2332
2333 return 0;
2334 }
2335}
2336]]>
2337okay
2338.
2339<![CDATA[
2340function matchwo(a,b)
2341{
2342 if (a < b && a < 0) then {
2343 return 1;
2344
2345 } else {
2346
2347 return 0;
2348 }
2349}
2350]]>
2351<p>okay</p>
2352.
2353
2354~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2355src line: 2973
2356
2357.
2358 <!-- foo -->
2359
2360 <!-- foo -->
2361.
2362 <!-- foo -->
2363<pre><code>&lt;!-- foo --&gt;
2364</code></pre>
2365.
2366
2367~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2368src line: 2984
2369
2370.
2371 <div>
2372
2373 <div>
2374.
2375 <div>
2376<pre><code>&lt;div&gt;
2377</code></pre>
2378.
2379
2380~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2381src line: 2998
2382
2383.
2384Foo
2385<div>
2386bar
2387</div>
2388.
2389<p>Foo</p>
2390<div>
2391bar
2392</div>
2393.
2394
2395~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2396src line: 3015
2397
2398.
2399<div>
2400bar
2401</div>
2402*foo*
2403.
2404<div>
2405bar
2406</div>
2407*foo*
2408.
2409
2410~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2411src line: 3030
2412
2413.
2414Foo
2415<a href="bar">
2416baz
2417.
2418<p>Foo
2419<a href="bar">
2420baz</p>
2421.
2422
2423~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2424src line: 3071
2425
2426.
2427<div>
2428
2429*Emphasized* text.
2430
2431</div>
2432.
2433<div>
2434<p><em>Emphasized</em> text.</p>
2435</div>
2436.
2437
2438~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2439src line: 3084
2440
2441.
2442<div>
2443*Emphasized* text.
2444</div>
2445.
2446<div>
2447*Emphasized* text.
2448</div>
2449.
2450
2451~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2452src line: 3106
2453
2454.
2455<table>
2456
2457<tr>
2458
2459<td>
2460Hi
2461</td>
2462
2463</tr>
2464
2465</table>
2466.
2467<table>
2468<tr>
2469<td>
2470Hi
2471</td>
2472</tr>
2473</table>
2474.
2475
2476~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2477src line: 3133
2478
2479.
2480<table>
2481
2482 <tr>
2483
2484 <td>
2485 Hi
2486 </td>
2487
2488 </tr>
2489
2490</table>
2491.
2492<table>
2493 <tr>
2494<pre><code>&lt;td&gt;
2495 Hi
2496&lt;/td&gt;
2497</code></pre>
2498 </tr>
2499</table>
2500.
2501
2502~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2503src line: 3182
2504
2505.
2506[foo]: /url "title"
2507
2508[foo]
2509.
2510<p><a href="/url" title="title">foo</a></p>
2511.
2512
2513~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2514src line: 3191
2515
2516.
2517 [foo]:
2518 /url
2519 'the title'
2520
2521[foo]
2522.
2523<p><a href="/url" title="the title">foo</a></p>
2524.
2525
2526~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2527src line: 3202
2528
2529.
2530[Foo*bar\]]:my_(url) 'title (with parens)'
2531
2532[Foo*bar\]]
2533.
2534<p><a href="my_(url)" title="title (with parens)">Foo*bar]</a></p>
2535.
2536
2537~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2538src line: 3211
2539
2540.
2541[Foo bar]:
2542<my url>
2543'title'
2544
2545[Foo bar]
2546.
2547<p><a href="my%20url" title="title">Foo bar</a></p>
2548.
2549
2550~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2551src line: 3224
2552
2553.
2554[foo]: /url '
2555title
2556line1
2557line2
2558'
2559
2560[foo]
2561.
2562<p><a href="/url" title="
2563title
2564line1
2565line2
2566">foo</a></p>
2567.
2568
2569~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2570src line: 3243
2571
2572.
2573[foo]: /url 'title
2574
2575with blank line'
2576
2577[foo]
2578.
2579<p>[foo]: /url 'title</p>
2580<p>with blank line'</p>
2581<p>[foo]</p>
2582.
2583
2584~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2585src line: 3258
2586
2587.
2588[foo]:
2589/url
2590
2591[foo]
2592.
2593<p><a href="/url">foo</a></p>
2594.
2595
2596~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2597src line: 3270
2598
2599.
2600[foo]:
2601
2602[foo]
2603.
2604<p>[foo]:</p>
2605<p>[foo]</p>
2606.
2607
2608~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2609src line: 3282
2610
2611.
2612[foo]: <>
2613
2614[foo]
2615.
2616<p><a href="">foo</a></p>
2617.
2618
2619~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2620src line: 3293
2621
2622.
2623[foo]: <bar>(baz)
2624
2625[foo]
2626.
2627<p>[foo]: <bar>(baz)</p>
2628<p>[foo]</p>
2629.
2630
2631~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2632src line: 3306
2633
2634.
2635[foo]: /url\bar\*baz "foo\"bar\baz"
2636
2637[foo]
2638.
2639<p><a href="/url%5Cbar*baz" title="foo&quot;bar\baz">foo</a></p>
2640.
2641
2642~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2643src line: 3317
2644
2645.
2646[foo]
2647
2648[foo]: url
2649.
2650<p><a href="url">foo</a></p>
2651.
2652
2653~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2654src line: 3329
2655
2656.
2657[foo]
2658
2659[foo]: first
2660[foo]: second
2661.
2662<p><a href="first">foo</a></p>
2663.
2664
2665~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2666src line: 3342
2667
2668.
2669[FOO]: /url
2670
2671[Foo]
2672.
2673<p><a href="/url">Foo</a></p>
2674.
2675
2676~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2677src line: 3351
2678
2679.
2680[ΑΓΩ]: /φου
2681
2682[αγω]
2683.
2684<p><a href="/%CF%86%CE%BF%CF%85">αγω</a></p>
2685.
2686
2687~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2688src line: 3366
2689
2690.
2691[foo]: /url
2692.
2693.
2694
2695~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2696src line: 3374
2697
2698.
2699[
2700foo
2701]: /url
2702bar
2703.
2704<p>bar</p>
2705.
2706
2707~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2708src line: 3387
2709
2710.
2711[foo]: /url "title" ok
2712.
2713<p>[foo]: /url &quot;title&quot; ok</p>
2714.
2715
2716~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2717src line: 3396
2718
2719.
2720[foo]: /url
2721"title" ok
2722.
2723<p>&quot;title&quot; ok</p>
2724.
2725
2726~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2727src line: 3407
2728
2729.
2730 [foo]: /url "title"
2731
2732[foo]
2733.
2734<pre><code>[foo]: /url &quot;title&quot;
2735</code></pre>
2736<p>[foo]</p>
2737.
2738
2739~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2740src line: 3421
2741
2742.
2743```
2744[foo]: /url
2745```
2746
2747[foo]
2748.
2749<pre><code>[foo]: /url
2750</code></pre>
2751<p>[foo]</p>
2752.
2753
2754~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2755src line: 3436
2756
2757.
2758Foo
2759[bar]: /baz
2760
2761[bar]
2762.
2763<p>Foo
2764[bar]: /baz</p>
2765<p>[bar]</p>
2766.
2767
2768~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2769src line: 3451
2770
2771.
2772# [Foo]
2773[foo]: /url
2774> bar
2775.
2776<h1><a href="/url">Foo</a></h1>
2777<blockquote>
2778<p>bar</p>
2779</blockquote>
2780.
2781
2782~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2783src line: 3462
2784
2785.
2786[foo]: /url
2787bar
2788===
2789[foo]
2790.
2791<h1>bar</h1>
2792<p><a href="/url">foo</a></p>
2793.
2794
2795~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2796src line: 3472
2797
2798.
2799[foo]: /url
2800===
2801[foo]
2802.
2803<p>===
2804<a href="/url">foo</a></p>
2805.
2806
2807~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2808src line: 3485
2809
2810.
2811[foo]: /foo-url "foo"
2812[bar]: /bar-url
2813 "bar"
2814[baz]: /baz-url
2815
2816[foo],
2817[bar],
2818[baz]
2819.
2820<p><a href="/foo-url" title="foo">foo</a>,
2821<a href="/bar-url" title="bar">bar</a>,
2822<a href="/baz-url">baz</a></p>
2823.
2824
2825~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2826src line: 3506
2827
2828.
2829[foo]
2830
2831> [foo]: /url
2832.
2833<p><a href="/url">foo</a></p>
2834<blockquote>
2835</blockquote>
2836.
2837
2838~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2839src line: 3528
2840
2841.
2842aaa
2843
2844bbb
2845.
2846<p>aaa</p>
2847<p>bbb</p>
2848.
2849
2850~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2851src line: 3540
2852
2853.
2854aaa
2855bbb
2856
2857ccc
2858ddd
2859.
2860<p>aaa
2861bbb</p>
2862<p>ccc
2863ddd</p>
2864.
2865
2866~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2867src line: 3556
2868
2869.
2870aaa
2871
2872
2873bbb
2874.
2875<p>aaa</p>
2876<p>bbb</p>
2877.
2878
2879~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2880src line: 3569
2881
2882.
2883 aaa
2884 bbb
2885.
2886<p>aaa
2887bbb</p>
2888.
2889
2890~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2891src line: 3581
2892
2893.
2894aaa
2895 bbb
2896 ccc
2897.
2898<p>aaa
2899bbb
2900ccc</p>
2901.
2902
2903~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2904src line: 3595
2905
2906.
2907 aaa
2908bbb
2909.
2910<p>aaa
2911bbb</p>
2912.
2913
2914~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2915src line: 3604
2916
2917.
2918 aaa
2919bbb
2920.
2921<pre><code>aaa
2922</code></pre>
2923<p>bbb</p>
2924.
2925
2926~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2927src line: 3618
2928
2929.
2930aaa
2931bbb
2932.
2933<p>aaa<br />
2934bbb</p>
2935.
2936
2937~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2938src line: 3635
2939
2940.
2941
2942
2943aaa
2944
2945
2946# aaa
2947
2948
2949.
2950<p>aaa</p>
2951<h1>aaa</h1>
2952.
2953
2954~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2955src line: 3703
2956
2957.
2958> # Foo
2959> bar
2960> baz
2961.
2962<blockquote>
2963<h1>Foo</h1>
2964<p>bar
2965baz</p>
2966</blockquote>
2967.
2968
2969~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2970src line: 3718
2971
2972.
2973># Foo
2974>bar
2975> baz
2976.
2977<blockquote>
2978<h1>Foo</h1>
2979<p>bar
2980baz</p>
2981</blockquote>
2982.
2983
2984~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2985src line: 3733
2986
2987.
2988 > # Foo
2989 > bar
2990 > baz
2991.
2992<blockquote>
2993<h1>Foo</h1>
2994<p>bar
2995baz</p>
2996</blockquote>
2997.
2998
2999~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3000src line: 3748
3001
3002.
3003 > # Foo
3004 > bar
3005 > baz
3006.
3007<pre><code>&gt; # Foo
3008&gt; bar
3009&gt; baz
3010</code></pre>
3011.
3012
3013~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3014src line: 3763
3015
3016.
3017> # Foo
3018> bar
3019baz
3020.
3021<blockquote>
3022<h1>Foo</h1>
3023<p>bar
3024baz</p>
3025</blockquote>
3026.
3027
3028~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3029src line: 3779
3030
3031.
3032> bar
3033baz
3034> foo
3035.
3036<blockquote>
3037<p>bar
3038baz
3039foo</p>
3040</blockquote>
3041.
3042
3043~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3044src line: 3803
3045
3046.
3047> foo
3048---
3049.
3050<blockquote>
3051<p>foo</p>
3052</blockquote>
3053<hr />
3054.
3055
3056~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3057src line: 3823
3058
3059.
3060> - foo
3061- bar
3062.
3063<blockquote>
3064<ul>
3065<li>foo</li>
3066</ul>
3067</blockquote>
3068<ul>
3069<li>bar</li>
3070</ul>
3071.
3072
3073~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3074src line: 3841
3075
3076.
3077> foo
3078 bar
3079.
3080<blockquote>
3081<pre><code>foo
3082</code></pre>
3083</blockquote>
3084<pre><code>bar
3085</code></pre>
3086.
3087
3088~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3089src line: 3854
3090
3091.
3092> ```
3093foo
3094```
3095.
3096<blockquote>
3097<pre><code></code></pre>
3098</blockquote>
3099<p>foo</p>
3100<pre><code></code></pre>
3101.
3102
3103~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3104src line: 3870
3105
3106.
3107> foo
3108 - bar
3109.
3110<blockquote>
3111<p>foo
3112- bar</p>
3113</blockquote>
3114.
3115
3116~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3117src line: 3894
3118
3119.
3120>
3121.
3122<blockquote>
3123</blockquote>
3124.
3125
3126~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3127src line: 3902
3128
3129.
3130>
3131>
3132>
3133.
3134<blockquote>
3135</blockquote>
3136.
3137
3138~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3139src line: 3914
3140
3141.
3142>
3143> foo
3144>
3145.
3146<blockquote>
3147<p>foo</p>
3148</blockquote>
3149.
3150
3151~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3152src line: 3927
3153
3154.
3155> foo
3156
3157> bar
3158.
3159<blockquote>
3160<p>foo</p>
3161</blockquote>
3162<blockquote>
3163<p>bar</p>
3164</blockquote>
3165.
3166
3167~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3168src line: 3949
3169
3170.
3171> foo
3172> bar
3173.
3174<blockquote>
3175<p>foo
3176bar</p>
3177</blockquote>
3178.
3179
3180~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3181src line: 3962
3182
3183.
3184> foo
3185>
3186> bar
3187.
3188<blockquote>
3189<p>foo</p>
3190<p>bar</p>
3191</blockquote>
3192.
3193
3194~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3195src line: 3976
3196
3197.
3198foo
3199> bar
3200.
3201<p>foo</p>
3202<blockquote>
3203<p>bar</p>
3204</blockquote>
3205.
3206
3207~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3208src line: 3990
3209
3210.
3211> aaa
3212***
3213> bbb
3214.
3215<blockquote>
3216<p>aaa</p>
3217</blockquote>
3218<hr />
3219<blockquote>
3220<p>bbb</p>
3221</blockquote>
3222.
3223
3224~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3225src line: 4008
3226
3227.
3228> bar
3229baz
3230.
3231<blockquote>
3232<p>bar
3233baz</p>
3234</blockquote>
3235.
3236
3237~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3238src line: 4019
3239
3240.
3241> bar
3242
3243baz
3244.
3245<blockquote>
3246<p>bar</p>
3247</blockquote>
3248<p>baz</p>
3249.
3250
3251~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3252src line: 4031
3253
3254.
3255> bar
3256>
3257baz
3258.
3259<blockquote>
3260<p>bar</p>
3261</blockquote>
3262<p>baz</p>
3263.
3264
3265~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3266src line: 4047
3267
3268.
3269> > > foo
3270bar
3271.
3272<blockquote>
3273<blockquote>
3274<blockquote>
3275<p>foo
3276bar</p>
3277</blockquote>
3278</blockquote>
3279</blockquote>
3280.
3281
3282~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3283src line: 4062
3284
3285.
3286>>> foo
3287> bar
3288>>baz
3289.
3290<blockquote>
3291<blockquote>
3292<blockquote>
3293<p>foo
3294bar
3295baz</p>
3296</blockquote>
3297</blockquote>
3298</blockquote>
3299.
3300
3301~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3302src line: 4084
3303
3304.
3305> code
3306
3307> not code
3308.
3309<blockquote>
3310<pre><code>code
3311</code></pre>
3312</blockquote>
3313<blockquote>
3314<p>not code</p>
3315</blockquote>
3316.
3317
3318~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3319src line: 4138
3320
3321.
3322A paragraph
3323with two lines.
3324
3325 indented code
3326
3327> A block quote.
3328.
3329<p>A paragraph
3330with two lines.</p>
3331<pre><code>indented code
3332</code></pre>
3333<blockquote>
3334<p>A block quote.</p>
3335</blockquote>
3336.
3337
3338~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3339src line: 4160
3340
3341.
33421. A paragraph
3343 with two lines.
3344
3345 indented code
3346
3347 > A block quote.
3348.
3349<ol>
3350<li>
3351<p>A paragraph
3352with two lines.</p>
3353<pre><code>indented code
3354</code></pre>
3355<blockquote>
3356<p>A block quote.</p>
3357</blockquote>
3358</li>
3359</ol>
3360.
3361
3362~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3363src line: 4193
3364
3365.
3366- one
3367
3368 two
3369.
3370<ul>
3371<li>one</li>
3372</ul>
3373<p>two</p>
3374.
3375
3376~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3377src line: 4205
3378
3379.
3380- one
3381
3382 two
3383.
3384<ul>
3385<li>
3386<p>one</p>
3387<p>two</p>
3388</li>
3389</ul>
3390.
3391
3392~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3393src line: 4219
3394
3395.
3396 - one
3397
3398 two
3399.
3400<ul>
3401<li>one</li>
3402</ul>
3403<pre><code> two
3404</code></pre>
3405.
3406
3407~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3408src line: 4232
3409
3410.
3411 - one
3412
3413 two
3414.
3415<ul>
3416<li>
3417<p>one</p>
3418<p>two</p>
3419</li>
3420</ul>
3421.
3422
3423~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3424src line: 4254
3425
3426.
3427 > > 1. one
3428>>
3429>> two
3430.
3431<blockquote>
3432<blockquote>
3433<ol>
3434<li>
3435<p>one</p>
3436<p>two</p>
3437</li>
3438</ol>
3439</blockquote>
3440</blockquote>
3441.
3442
3443~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3444src line: 4281
3445
3446.
3447>>- one
3448>>
3449 > > two
3450.
3451<blockquote>
3452<blockquote>
3453<ul>
3454<li>one</li>
3455</ul>
3456<p>two</p>
3457</blockquote>
3458</blockquote>
3459.
3460
3461~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3462src line: 4300
3463
3464.
3465-one
3466
34672.two
3468.
3469<p>-one</p>
3470<p>2.two</p>
3471.
3472
3473~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3474src line: 4313
3475
3476.
3477- foo
3478
3479
3480 bar
3481.
3482<ul>
3483<li>
3484<p>foo</p>
3485<p>bar</p>
3486</li>
3487</ul>
3488.
3489
3490~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3491src line: 4330
3492
3493.
34941. foo
3495
3496 ```
3497 bar
3498 ```
3499
3500 baz
3501
3502 > bam
3503.
3504<ol>
3505<li>
3506<p>foo</p>
3507<pre><code>bar
3508</code></pre>
3509<p>baz</p>
3510<blockquote>
3511<p>bam</p>
3512</blockquote>
3513</li>
3514</ol>
3515.
3516
3517~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3518src line: 4358
3519
3520.
3521- Foo
3522
3523 bar
3524
3525
3526 baz
3527.
3528<ul>
3529<li>
3530<p>Foo</p>
3531<pre><code>bar
3532
3533
3534baz
3535</code></pre>
3536</li>
3537</ul>
3538.
3539
3540~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3541src line: 4380
3542
3543.
3544123456789. ok
3545.
3546<ol start="123456789">
3547<li>ok</li>
3548</ol>
3549.
3550
3551~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3552src line: 4389
3553
3554.
35551234567890. not ok
3556.
3557<p>1234567890. not ok</p>
3558.
3559
3560~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3561src line: 4398
3562
3563.
35640. ok
3565.
3566<ol start="0">
3567<li>ok</li>
3568</ol>
3569.
3570
3571~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3572src line: 4407
3573
3574.
3575003. ok
3576.
3577<ol start="3">
3578<li>ok</li>
3579</ol>
3580.
3581
3582~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3583src line: 4418
3584
3585.
3586-1. not ok
3587.
3588<p>-1. not ok</p>
3589.
3590
3591~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3592src line: 4441
3593
3594.
3595- foo
3596
3597 bar
3598.
3599<ul>
3600<li>
3601<p>foo</p>
3602<pre><code>bar
3603</code></pre>
3604</li>
3605</ul>
3606.
3607
3608~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3609src line: 4458
3610
3611.
3612 10. foo
3613
3614 bar
3615.
3616<ol start="10">
3617<li>
3618<p>foo</p>
3619<pre><code>bar
3620</code></pre>
3621</li>
3622</ol>
3623.
3624
3625~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3626src line: 4477
3627
3628.
3629 indented code
3630
3631paragraph
3632
3633 more code
3634.
3635<pre><code>indented code
3636</code></pre>
3637<p>paragraph</p>
3638<pre><code>more code
3639</code></pre>
3640.
3641
3642~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3643src line: 4492
3644
3645.
36461. indented code
3647
3648 paragraph
3649
3650 more code
3651.
3652<ol>
3653<li>
3654<pre><code>indented code
3655</code></pre>
3656<p>paragraph</p>
3657<pre><code>more code
3658</code></pre>
3659</li>
3660</ol>
3661.
3662
3663~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3664src line: 4514
3665
3666.
36671. indented code
3668
3669 paragraph
3670
3671 more code
3672.
3673<ol>
3674<li>
3675<pre><code> indented code
3676</code></pre>
3677<p>paragraph</p>
3678<pre><code>more code
3679</code></pre>
3680</li>
3681</ol>
3682.
3683
3684~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3685src line: 4541
3686
3687.
3688 foo
3689
3690bar
3691.
3692<p>foo</p>
3693<p>bar</p>
3694.
3695
3696~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3697src line: 4551
3698
3699.
3700- foo
3701
3702 bar
3703.
3704<ul>
3705<li>foo</li>
3706</ul>
3707<p>bar</p>
3708.
3709
3710~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3711src line: 4568
3712
3713.
3714- foo
3715
3716 bar
3717.
3718<ul>
3719<li>
3720<p>foo</p>
3721<p>bar</p>
3722</li>
3723</ul>
3724.
3725
3726~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3727src line: 4595
3728
3729.
3730-
3731 foo
3732-
3733 ```
3734 bar
3735 ```
3736-
3737 baz
3738.
3739<ul>
3740<li>foo</li>
3741<li>
3742<pre><code>bar
3743</code></pre>
3744</li>
3745<li>
3746<pre><code>baz
3747</code></pre>
3748</li>
3749</ul>
3750.
3751
3752~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3753src line: 4621
3754
3755.
3756-
3757 foo
3758.
3759<ul>
3760<li>foo</li>
3761</ul>
3762.
3763
3764~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3765src line: 4635
3766
3767.
3768-
3769
3770 foo
3771.
3772<ul>
3773<li></li>
3774</ul>
3775<p>foo</p>
3776.
3777
3778~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3779src line: 4649
3780
3781.
3782- foo
3783-
3784- bar
3785.
3786<ul>
3787<li>foo</li>
3788<li></li>
3789<li>bar</li>
3790</ul>
3791.
3792
3793~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3794src line: 4664
3795
3796.
3797- foo
3798-
3799- bar
3800.
3801<ul>
3802<li>foo</li>
3803<li></li>
3804<li>bar</li>
3805</ul>
3806.
3807
3808~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3809src line: 4679
3810
3811.
38121. foo
38132.
38143. bar
3815.
3816<ol>
3817<li>foo</li>
3818<li></li>
3819<li>bar</li>
3820</ol>
3821.
3822
3823~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3824src line: 4694
3825
3826.
3827*
3828.
3829<ul>
3830<li></li>
3831</ul>
3832.
3833
3834~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3835src line: 4704
3836
3837.
3838foo
3839*
3840
3841foo
38421.
3843.
3844<p>foo
3845*</p>
3846<p>foo
38471.</p>
3848.
3849
3850~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3851src line: 4726
3852
3853.
3854 1. A paragraph
3855 with two lines.
3856
3857 indented code
3858
3859 > A block quote.
3860.
3861<ol>
3862<li>
3863<p>A paragraph
3864with two lines.</p>
3865<pre><code>indented code
3866</code></pre>
3867<blockquote>
3868<p>A block quote.</p>
3869</blockquote>
3870</li>
3871</ol>
3872.
3873
3874~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3875src line: 4750
3876
3877.
3878 1. A paragraph
3879 with two lines.
3880
3881 indented code
3882
3883 > A block quote.
3884.
3885<ol>
3886<li>
3887<p>A paragraph
3888with two lines.</p>
3889<pre><code>indented code
3890</code></pre>
3891<blockquote>
3892<p>A block quote.</p>
3893</blockquote>
3894</li>
3895</ol>
3896.
3897
3898~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3899src line: 4774
3900
3901.
3902 1. A paragraph
3903 with two lines.
3904
3905 indented code
3906
3907 > A block quote.
3908.
3909<ol>
3910<li>
3911<p>A paragraph
3912with two lines.</p>
3913<pre><code>indented code
3914</code></pre>
3915<blockquote>
3916<p>A block quote.</p>
3917</blockquote>
3918</li>
3919</ol>
3920.
3921
3922~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3923src line: 4798
3924
3925.
3926 1. A paragraph
3927 with two lines.
3928
3929 indented code
3930
3931 > A block quote.
3932.
3933<pre><code>1. A paragraph
3934 with two lines.
3935
3936 indented code
3937
3938 &gt; A block quote.
3939</code></pre>
3940.
3941
3942~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3943src line: 4828
3944
3945.
3946 1. A paragraph
3947with two lines.
3948
3949 indented code
3950
3951 > A block quote.
3952.
3953<ol>
3954<li>
3955<p>A paragraph
3956with two lines.</p>
3957<pre><code>indented code
3958</code></pre>
3959<blockquote>
3960<p>A block quote.</p>
3961</blockquote>
3962</li>
3963</ol>
3964.
3965
3966~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3967src line: 4852
3968
3969.
3970 1. A paragraph
3971 with two lines.
3972.
3973<ol>
3974<li>A paragraph
3975with two lines.</li>
3976</ol>
3977.
3978
3979~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3980src line: 4865
3981
3982.
3983> 1. > Blockquote
3984continued here.
3985.
3986<blockquote>
3987<ol>
3988<li>
3989<blockquote>
3990<p>Blockquote
3991continued here.</p>
3992</blockquote>
3993</li>
3994</ol>
3995</blockquote>
3996.
3997
3998~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3999src line: 4882
4000
4001.
4002> 1. > Blockquote
4003> continued here.
4004.
4005<blockquote>
4006<ol>
4007<li>
4008<blockquote>
4009<p>Blockquote
4010continued here.</p>
4011</blockquote>
4012</li>
4013</ol>
4014</blockquote>
4015.
4016
4017~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4018src line: 4910
4019
4020.
4021- foo
4022 - bar
4023 - baz
4024 - boo
4025.
4026<ul>
4027<li>foo
4028<ul>
4029<li>bar
4030<ul>
4031<li>baz
4032<ul>
4033<li>boo</li>
4034</ul>
4035</li>
4036</ul>
4037</li>
4038</ul>
4039</li>
4040</ul>
4041.
4042
4043~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4044src line: 4936
4045
4046.
4047- foo
4048 - bar
4049 - baz
4050 - boo
4051.
4052<ul>
4053<li>foo</li>
4054<li>bar</li>
4055<li>baz</li>
4056<li>boo</li>
4057</ul>
4058.
4059
4060~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4061src line: 4953
4062
4063.
406410) foo
4065 - bar
4066.
4067<ol start="10">
4068<li>foo
4069<ul>
4070<li>bar</li>
4071</ul>
4072</li>
4073</ol>
4074.
4075
4076~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4077src line: 4969
4078
4079.
408010) foo
4081 - bar
4082.
4083<ol start="10">
4084<li>foo</li>
4085</ol>
4086<ul>
4087<li>bar</li>
4088</ul>
4089.
4090
4091~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4092src line: 4984
4093
4094.
4095- - foo
4096.
4097<ul>
4098<li>
4099<ul>
4100<li>foo</li>
4101</ul>
4102</li>
4103</ul>
4104.
4105
4106~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4107src line: 4997
4108
4109.
41101. - 2. foo
4111.
4112<ol>
4113<li>
4114<ul>
4115<li>
4116<ol start="2">
4117<li>foo</li>
4118</ol>
4119</li>
4120</ul>
4121</li>
4122</ol>
4123.
4124
4125~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4126src line: 5016
4127
4128.
4129- # Foo
4130- Bar
4131 ---
4132 baz
4133.
4134<ul>
4135<li>
4136<h1>Foo</h1>
4137</li>
4138<li>
4139<h2>Bar</h2>
4140baz</li>
4141</ul>
4142.
4143
4144~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4145src line: 5252
4146
4147.
4148- foo
4149- bar
4150+ baz
4151.
4152<ul>
4153<li>foo</li>
4154<li>bar</li>
4155</ul>
4156<ul>
4157<li>baz</li>
4158</ul>
4159.
4160
4161~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4162src line: 5267
4163
4164.
41651. foo
41662. bar
41673) baz
4168.
4169<ol>
4170<li>foo</li>
4171<li>bar</li>
4172</ol>
4173<ol start="3">
4174<li>baz</li>
4175</ol>
4176.
4177
4178~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4179src line: 5286
4180
4181.
4182Foo
4183- bar
4184- baz
4185.
4186<p>Foo</p>
4187<ul>
4188<li>bar</li>
4189<li>baz</li>
4190</ul>
4191.
4192
4193~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4194src line: 5363
4195
4196.
4197The number of windows in my house is
419814. The number of doors is 6.
4199.
4200<p>The number of windows in my house is
420114. The number of doors is 6.</p>
4202.
4203
4204~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4205src line: 5373
4206
4207.
4208The number of windows in my house is
42091. The number of doors is 6.
4210.
4211<p>The number of windows in my house is</p>
4212<ol>
4213<li>The number of doors is 6.</li>
4214</ol>
4215.
4216
4217~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4218src line: 5387
4219
4220.
4221- foo
4222
4223- bar
4224
4225
4226- baz
4227.
4228<ul>
4229<li>
4230<p>foo</p>
4231</li>
4232<li>
4233<p>bar</p>
4234</li>
4235<li>
4236<p>baz</p>
4237</li>
4238</ul>
4239.
4240
4241~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4242src line: 5408
4243
4244.
4245- foo
4246 - bar
4247 - baz
4248
4249
4250 bim
4251.
4252<ul>
4253<li>foo
4254<ul>
4255<li>bar
4256<ul>
4257<li>
4258<p>baz</p>
4259<p>bim</p>
4260</li>
4261</ul>
4262</li>
4263</ul>
4264</li>
4265</ul>
4266.
4267
4268~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4269src line: 5438
4270
4271.
4272- foo
4273- bar
4274
4275<!-- -->
4276
4277- baz
4278- bim
4279.
4280<ul>
4281<li>foo</li>
4282<li>bar</li>
4283</ul>
4284<!-- -->
4285<ul>
4286<li>baz</li>
4287<li>bim</li>
4288</ul>
4289.
4290
4291~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4292src line: 5459
4293
4294.
4295- foo
4296
4297 notcode
4298
4299- foo
4300
4301<!-- -->
4302
4303 code
4304.
4305<ul>
4306<li>
4307<p>foo</p>
4308<p>notcode</p>
4309</li>
4310<li>
4311<p>foo</p>
4312</li>
4313</ul>
4314<!-- -->
4315<pre><code>code
4316</code></pre>
4317.
4318
4319~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4320src line: 5490
4321
4322.
4323- a
4324 - b
4325 - c
4326 - d
4327 - e
4328 - f
4329- g
4330.
4331<ul>
4332<li>a</li>
4333<li>b</li>
4334<li>c</li>
4335<li>d</li>
4336<li>e</li>
4337<li>f</li>
4338<li>g</li>
4339</ul>
4340.
4341
4342~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4343src line: 5511
4344
4345.
43461. a
4347
4348 2. b
4349
4350 3. c
4351.
4352<ol>
4353<li>
4354<p>a</p>
4355</li>
4356<li>
4357<p>b</p>
4358</li>
4359<li>
4360<p>c</p>
4361</li>
4362</ol>
4363.
4364
4365~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4366src line: 5535
4367
4368.
4369- a
4370 - b
4371 - c
4372 - d
4373 - e
4374.
4375<ul>
4376<li>a</li>
4377<li>b</li>
4378<li>c</li>
4379<li>d
4380- e</li>
4381</ul>
4382.
4383
4384~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4385src line: 5555
4386
4387.
43881. a
4389
4390 2. b
4391
4392 3. c
4393.
4394<ol>
4395<li>
4396<p>a</p>
4397</li>
4398<li>
4399<p>b</p>
4400</li>
4401</ol>
4402<pre><code>3. c
4403</code></pre>
4404.
4405
4406~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4407src line: 5578
4408
4409.
4410- a
4411- b
4412
4413- c
4414.
4415<ul>
4416<li>
4417<p>a</p>
4418</li>
4419<li>
4420<p>b</p>
4421</li>
4422<li>
4423<p>c</p>
4424</li>
4425</ul>
4426.
4427
4428~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4429src line: 5600
4430
4431.
4432* a
4433*
4434
4435* c
4436.
4437<ul>
4438<li>
4439<p>a</p>
4440</li>
4441<li></li>
4442<li>
4443<p>c</p>
4444</li>
4445</ul>
4446.
4447
4448~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4449src line: 5622
4450
4451.
4452- a
4453- b
4454
4455 c
4456- d
4457.
4458<ul>
4459<li>
4460<p>a</p>
4461</li>
4462<li>
4463<p>b</p>
4464<p>c</p>
4465</li>
4466<li>
4467<p>d</p>
4468</li>
4469</ul>
4470.
4471
4472~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4473src line: 5644
4474
4475.
4476- a
4477- b
4478
4479 [ref]: /url
4480- d
4481.
4482<ul>
4483<li>
4484<p>a</p>
4485</li>
4486<li>
4487<p>b</p>
4488</li>
4489<li>
4490<p>d</p>
4491</li>
4492</ul>
4493.
4494
4495~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4496src line: 5667
4497
4498.
4499- a
4500- ```
4501 b
4502
4503
4504 ```
4505- c
4506.
4507<ul>
4508<li>a</li>
4509<li>
4510<pre><code>b
4511
4512
4513</code></pre>
4514</li>
4515<li>c</li>
4516</ul>
4517.
4518
4519~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4520src line: 5693
4521
4522.
4523- a
4524 - b
4525
4526 c
4527- d
4528.
4529<ul>
4530<li>a
4531<ul>
4532<li>
4533<p>b</p>
4534<p>c</p>
4535</li>
4536</ul>
4537</li>
4538<li>d</li>
4539</ul>
4540.
4541
4542~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4543src line: 5717
4544
4545.
4546* a
4547 > b
4548 >
4549* c
4550.
4551<ul>
4552<li>a
4553<blockquote>
4554<p>b</p>
4555</blockquote>
4556</li>
4557<li>c</li>
4558</ul>
4559.
4560
4561~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4562src line: 5737
4563
4564.
4565- a
4566 > b
4567 ```
4568 c
4569 ```
4570- d
4571.
4572<ul>
4573<li>a
4574<blockquote>
4575<p>b</p>
4576</blockquote>
4577<pre><code>c
4578</code></pre>
4579</li>
4580<li>d</li>
4581</ul>
4582.
4583
4584~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4585src line: 5760
4586
4587.
4588- a
4589.
4590<ul>
4591<li>a</li>
4592</ul>
4593.
4594
4595~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4596src line: 5769
4597
4598.
4599- a
4600 - b
4601.
4602<ul>
4603<li>a
4604<ul>
4605<li>b</li>
4606</ul>
4607</li>
4608</ul>
4609.
4610
4611~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4612src line: 5786
4613
4614.
46151. ```
4616 foo
4617 ```
4618
4619 bar
4620.
4621<ol>
4622<li>
4623<pre><code>foo
4624</code></pre>
4625<p>bar</p>
4626</li>
4627</ol>
4628.
4629
4630~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4631src line: 5805
4632
4633.
4634* foo
4635 * bar
4636
4637 baz
4638.
4639<ul>
4640<li>
4641<p>foo</p>
4642<ul>
4643<li>bar</li>
4644</ul>
4645<p>baz</p>
4646</li>
4647</ul>
4648.
4649
4650~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4651src line: 5823
4652
4653.
4654- a
4655 - b
4656 - c
4657
4658- d
4659 - e
4660 - f
4661.
4662<ul>
4663<li>
4664<p>a</p>
4665<ul>
4666<li>b</li>
4667<li>c</li>
4668</ul>
4669</li>
4670<li>
4671<p>d</p>
4672<ul>
4673<li>e</li>
4674<li>f</li>
4675</ul>
4676</li>
4677</ul>
4678.
4679
4680~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4681src line: 5857
4682
4683.
4684`hi`lo`
4685.
4686<p><code>hi</code>lo`</p>
4687.
4688
4689~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4690src line: 5889
4691
4692.
4693`foo`
4694.
4695<p><code>foo</code></p>
4696.
4697
4698~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4699src line: 5900
4700
4701.
4702`` foo ` bar ``
4703.
4704<p><code>foo ` bar</code></p>
4705.
4706
4707~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4708src line: 5910
4709
4710.
4711` `` `
4712.
4713<p><code>``</code></p>
4714.
4715
4716~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4717src line: 5918
4718
4719.
4720` `` `
4721.
4722<p><code> `` </code></p>
4723.
4724
4725~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4726src line: 5927
4727
4728.
4729` a`
4730.
4731<p><code> a</code></p>
4732.
4733
4734~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4735src line: 5936
4736
4737.
4738` b `
4739.
4740<p><code> b </code></p>
4741.
4742
4743~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4744src line: 5944
4745
4746.
4747` `
4748` `
4749.
4750<p><code> </code>
4751<code> </code></p>
4752.
4753
4754~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4755src line: 5955
4756
4757.
4758``
4759foo
4760bar
4761baz
4762``
4763.
4764<p><code>foo bar baz</code></p>
4765.
4766
4767~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4768src line: 5965
4769
4770.
4771``
4772foo
4773``
4774.
4775<p><code>foo </code></p>
4776.
4777
4778~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4779src line: 5976
4780
4781.
4782`foo bar
4783baz`
4784.
4785<p><code>foo bar baz</code></p>
4786.
4787
4788~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4789src line: 5993
4790
4791.
4792`foo\`bar`
4793.
4794<p><code>foo\</code>bar`</p>
4795.
4796
4797~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4798src line: 6004
4799
4800.
4801``foo`bar``
4802.
4803<p><code>foo`bar</code></p>
4804.
4805
4806~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4807src line: 6010
4808
4809.
4810` foo `` bar `
4811.
4812<p><code>foo `` bar</code></p>
4813.
4814
4815~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4816src line: 6022
4817
4818.
4819*foo`*`
4820.
4821<p>*foo<code>*</code></p>
4822.
4823
4824~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4825src line: 6031
4826
4827.
4828[not a `link](/foo`)
4829.
4830<p>[not a <code>link](/foo</code>)</p>
4831.
4832
4833~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4834src line: 6041
4835
4836.
4837`<a href="`">`
4838.
4839<p><code>&lt;a href=&quot;</code>&quot;&gt;`</p>
4840.
4841
4842~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4843src line: 6050
4844
4845.
4846<a href="`">`
4847.
4848<p><a href="`">`</p>
4849.
4850
4851~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4852src line: 6059
4853
4854.
4855`<http://foo.bar.`baz>`
4856.
4857<p><code>&lt;http://foo.bar.</code>baz&gt;`</p>
4858.
4859
4860~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4861src line: 6068
4862
4863.
4864<http://foo.bar.`baz>`
4865.
4866<p><a href="http://foo.bar.%60baz">http://foo.bar.`baz</a>`</p>
4867.
4868
4869~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4870src line: 6078
4871
4872.
4873```foo``
4874.
4875<p>```foo``</p>
4876.
4877
4878~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4879src line: 6085
4880
4881.
4882`foo
4883.
4884<p>`foo</p>
4885.
4886
4887~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4888src line: 6094
4889
4890.
4891`foo``bar``
4892.
4893<p>`foo<code>bar</code></p>
4894.
4895
4896~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4897src line: 6311
4898
4899.
4900*foo bar*
4901.
4902<p><em>foo bar</em></p>
4903.
4904
4905~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4906src line: 6321
4907
4908.
4909a * foo bar*
4910.
4911<p>a * foo bar*</p>
4912.
4913
4914~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4915src line: 6332
4916
4917.
4918a*"foo"*
4919.
4920<p>a*&quot;foo&quot;*</p>
4921.
4922
4923~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4924src line: 6341
4925
4926.
4927* a *
4928.
4929<p>* a *</p>
4930.
4931
4932~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4933src line: 6350
4934
4935.
4936foo*bar*
4937.
4938<p>foo<em>bar</em></p>
4939.
4940
4941~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4942src line: 6357
4943
4944.
49455*6*78
4946.
4947<p>5<em>6</em>78</p>
4948.
4949
4950~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4951src line: 6366
4952
4953.
4954_foo bar_
4955.
4956<p><em>foo bar</em></p>
4957.
4958
4959~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4960src line: 6376
4961
4962.
4963_ foo bar_
4964.
4965<p>_ foo bar_</p>
4966.
4967
4968~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4969src line: 6386
4970
4971.
4972a_"foo"_
4973.
4974<p>a_&quot;foo&quot;_</p>
4975.
4976
4977~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4978src line: 6395
4979
4980.
4981foo_bar_
4982.
4983<p>foo_bar_</p>
4984.
4985
4986~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4987src line: 6402
4988
4989.
49905_6_78
4991.
4992<p>5_6_78</p>
4993.
4994
4995~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4996src line: 6409
4997
4998.
4999пристаням_стремятся_
5000.
5001<p>пристаням_стремятся_</p>
5002.
5003
5004~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5005src line: 6419
5006
5007.
5008aa_"bb"_cc
5009.
5010<p>aa_&quot;bb&quot;_cc</p>
5011.
5012
5013~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5014src line: 6430
5015
5016.
5017foo-_(bar)_
5018.
5019<p>foo-<em>(bar)</em></p>
5020.
5021
5022~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5023src line: 6442
5024
5025.
5026_foo*
5027.
5028<p>_foo*</p>
5029.
5030
5031~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5032src line: 6452
5033
5034.
5035*foo bar *
5036.
5037<p>*foo bar *</p>
5038.
5039
5040~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5041src line: 6461
5042
5043.
5044*foo bar
5045*
5046.
5047<p>*foo bar
5048*</p>
5049.
5050
5051~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5052src line: 6474
5053
5054.
5055*(*foo)
5056.
5057<p>*(*foo)</p>
5058.
5059
5060~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5061src line: 6484
5062
5063.
5064*(*foo*)*
5065.
5066<p><em>(<em>foo</em>)</em></p>
5067.
5068
5069~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5070src line: 6493
5071
5072.
5073*foo*bar
5074.
5075<p><em>foo</em>bar</p>
5076.
5077
5078~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5079src line: 6506
5080
5081.
5082_foo bar _
5083.
5084<p>_foo bar _</p>
5085.
5086
5087~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5088src line: 6516
5089
5090.
5091_(_foo)
5092.
5093<p>_(_foo)</p>
5094.
5095
5096~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5097src line: 6525
5098
5099.
5100_(_foo_)_
5101.
5102<p><em>(<em>foo</em>)</em></p>
5103.
5104
5105~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5106src line: 6534
5107
5108.
5109_foo_bar
5110.
5111<p>_foo_bar</p>
5112.
5113
5114~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5115src line: 6541
5116
5117.
5118_пристаням_стремятся
5119.
5120<p>_пристаням_стремятся</p>
5121.
5122
5123~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5124src line: 6548
5125
5126.
5127_foo_bar_baz_
5128.
5129<p><em>foo_bar_baz</em></p>
5130.
5131
5132~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5133src line: 6559
5134
5135.
5136_(bar)_.
5137.
5138<p><em>(bar)</em>.</p>
5139.
5140
5141~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5142src line: 6568
5143
5144.
5145**foo bar**
5146.
5147<p><strong>foo bar</strong></p>
5148.
5149
5150~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5151src line: 6578
5152
5153.
5154** foo bar**
5155.
5156<p>** foo bar**</p>
5157.
5158
5159~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5160src line: 6589
5161
5162.
5163a**"foo"**
5164.
5165<p>a**&quot;foo&quot;**</p>
5166.
5167
5168~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5169src line: 6598
5170
5171.
5172foo**bar**
5173.
5174<p>foo<strong>bar</strong></p>
5175.
5176
5177~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5178src line: 6607
5179
5180.
5181__foo bar__
5182.
5183<p><strong>foo bar</strong></p>
5184.
5185
5186~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5187src line: 6617
5188
5189.
5190__ foo bar__
5191.
5192<p>__ foo bar__</p>
5193.
5194
5195~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5196src line: 6625
5197
5198.
5199__
5200foo bar__
5201.
5202<p>__
5203foo bar__</p>
5204.
5205
5206~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5207src line: 6637
5208
5209.
5210a__"foo"__
5211.
5212<p>a__&quot;foo&quot;__</p>
5213.
5214
5215~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5216src line: 6646
5217
5218.
5219foo__bar__
5220.
5221<p>foo__bar__</p>
5222.
5223
5224~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5225src line: 6653
5226
5227.
52285__6__78
5229.
5230<p>5__6__78</p>
5231.
5232
5233~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5234src line: 6660
5235
5236.
5237пристаням__стремятся__
5238.
5239<p>пристаням__стремятся__</p>
5240.
5241
5242~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5243src line: 6667
5244
5245.
5246__foo, __bar__, baz__
5247.
5248<p><strong>foo, <strong>bar</strong>, baz</strong></p>
5249.
5250
5251~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5252src line: 6678
5253
5254.
5255foo-__(bar)__
5256.
5257<p>foo-<strong>(bar)</strong></p>
5258.
5259
5260~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5261src line: 6691
5262
5263.
5264**foo bar **
5265.
5266<p>**foo bar **</p>
5267.
5268
5269~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5270src line: 6704
5271
5272.
5273**(**foo)
5274.
5275<p>**(**foo)</p>
5276.
5277
5278~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5279src line: 6714
5280
5281.
5282*(**foo**)*
5283.
5284<p><em>(<strong>foo</strong>)</em></p>
5285.
5286
5287~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5288src line: 6721
5289
5290.
5291**Gomphocarpus (*Gomphocarpus physocarpus*, syn.
5292*Asclepias physocarpa*)**
5293.
5294<p><strong>Gomphocarpus (<em>Gomphocarpus physocarpus</em>, syn.
5295<em>Asclepias physocarpa</em>)</strong></p>
5296.
5297
5298~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5299src line: 6730
5300
5301.
5302**foo "*bar*" foo**
5303.
5304<p><strong>foo &quot;<em>bar</em>&quot; foo</strong></p>
5305.
5306
5307~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5308src line: 6739
5309
5310.
5311**foo**bar
5312.
5313<p><strong>foo</strong>bar</p>
5314.
5315
5316~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5317src line: 6751
5318
5319.
5320__foo bar __
5321.
5322<p>__foo bar __</p>
5323.
5324
5325~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5326src line: 6761
5327
5328.
5329__(__foo)
5330.
5331<p>__(__foo)</p>
5332.
5333
5334~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5335src line: 6771
5336
5337.
5338_(__foo__)_
5339.
5340<p><em>(<strong>foo</strong>)</em></p>
5341.
5342
5343~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5344src line: 6780
5345
5346.
5347__foo__bar
5348.
5349<p>__foo__bar</p>
5350.
5351
5352~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5353src line: 6787
5354
5355.
5356__пристаням__стремятся
5357.
5358<p>__пристаням__стремятся</p>
5359.
5360
5361~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5362src line: 6794
5363
5364.
5365__foo__bar__baz__
5366.
5367<p><strong>foo__bar__baz</strong></p>
5368.
5369
5370~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5371src line: 6805
5372
5373.
5374__(bar)__.
5375.
5376<p><strong>(bar)</strong>.</p>
5377.
5378
5379~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5380src line: 6817
5381
5382.
5383*foo [bar](/url)*
5384.
5385<p><em>foo <a href="/url">bar</a></em></p>
5386.
5387
5388~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5389src line: 6824
5390
5391.
5392*foo
5393bar*
5394.
5395<p><em>foo
5396bar</em></p>
5397.
5398
5399~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5400src line: 6836
5401
5402.
5403_foo __bar__ baz_
5404.
5405<p><em>foo <strong>bar</strong> baz</em></p>
5406.
5407
5408~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5409src line: 6843
5410
5411.
5412_foo _bar_ baz_
5413.
5414<p><em>foo <em>bar</em> baz</em></p>
5415.
5416
5417~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5418src line: 6850
5419
5420.
5421__foo_ bar_
5422.
5423<p><em><em>foo</em> bar</em></p>
5424.
5425
5426~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5427src line: 6857
5428
5429.
5430*foo *bar**
5431.
5432<p><em>foo <em>bar</em></em></p>
5433.
5434
5435~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5436src line: 6864
5437
5438.
5439*foo **bar** baz*
5440.
5441<p><em>foo <strong>bar</strong> baz</em></p>
5442.
5443
5444~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5445src line: 6870
5446
5447.
5448*foo**bar**baz*
5449.
5450<p><em>foo<strong>bar</strong>baz</em></p>
5451.
5452
5453~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5454src line: 6894
5455
5456.
5457*foo**bar*
5458.
5459<p><em>foo**bar</em></p>
5460.
5461
5462~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5463src line: 6907
5464
5465.
5466***foo** bar*
5467.
5468<p><em><strong>foo</strong> bar</em></p>
5469.
5470
5471~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5472src line: 6914
5473
5474.
5475*foo **bar***
5476.
5477<p><em>foo <strong>bar</strong></em></p>
5478.
5479
5480~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5481src line: 6921
5482
5483.
5484*foo**bar***
5485.
5486<p><em>foo<strong>bar</strong></em></p>
5487.
5488
5489~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5490src line: 6932
5491
5492.
5493foo***bar***baz
5494.
5495<p>foo<em><strong>bar</strong></em>baz</p>
5496.
5497
5498~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5499src line: 6938
5500
5501.
5502foo******bar*********baz
5503.
5504<p>foo<strong><strong><strong>bar</strong></strong></strong>***baz</p>
5505.
5506
5507~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5508src line: 6947
5509
5510.
5511*foo **bar *baz* bim** bop*
5512.
5513<p><em>foo <strong>bar <em>baz</em> bim</strong> bop</em></p>
5514.
5515
5516~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5517src line: 6954
5518
5519.
5520*foo [*bar*](/url)*
5521.
5522<p><em>foo <a href="/url"><em>bar</em></a></em></p>
5523.
5524
5525~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5526src line: 6963
5527
5528.
5529** is not an empty emphasis
5530.
5531<p>** is not an empty emphasis</p>
5532.
5533
5534~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5535src line: 6970
5536
5537.
5538**** is not an empty strong emphasis
5539.
5540<p>**** is not an empty strong emphasis</p>
5541.
5542
5543~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5544src line: 6983
5545
5546.
5547**foo [bar](/url)**
5548.
5549<p><strong>foo <a href="/url">bar</a></strong></p>
5550.
5551
5552~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5553src line: 6990
5554
5555.
5556**foo
5557bar**
5558.
5559<p><strong>foo
5560bar</strong></p>
5561.
5562
5563~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5564src line: 7002
5565
5566.
5567__foo _bar_ baz__
5568.
5569<p><strong>foo <em>bar</em> baz</strong></p>
5570.
5571
5572~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5573src line: 7009
5574
5575.
5576__foo __bar__ baz__
5577.
5578<p><strong>foo <strong>bar</strong> baz</strong></p>
5579.
5580
5581~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5582src line: 7016
5583
5584.
5585____foo__ bar__
5586.
5587<p><strong><strong>foo</strong> bar</strong></p>
5588.
5589
5590~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5591src line: 7023
5592
5593.
5594**foo **bar****
5595.
5596<p><strong>foo <strong>bar</strong></strong></p>
5597.
5598
5599~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5600src line: 7030
5601
5602.
5603**foo *bar* baz**
5604.
5605<p><strong>foo <em>bar</em> baz</strong></p>
5606.
5607
5608~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5609src line: 7037
5610
5611.
5612**foo*bar*baz**
5613.
5614<p><strong>foo<em>bar</em>baz</strong></p>
5615.
5616
5617~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5618src line: 7044
5619
5620.
5621***foo* bar**
5622.
5623<p><strong><em>foo</em> bar</strong></p>
5624.
5625
5626~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5627src line: 7051
5628
5629.
5630**foo *bar***
5631.
5632<p><strong>foo <em>bar</em></strong></p>
5633.
5634
5635~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5636src line: 7060
5637
5638.
5639**foo *bar **baz**
5640bim* bop**
5641.
5642<p><strong>foo <em>bar <strong>baz</strong>
5643bim</em> bop</strong></p>
5644.
5645
5646~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5647src line: 7069
5648
5649.
5650**foo [*bar*](/url)**
5651.
5652<p><strong>foo <a href="/url"><em>bar</em></a></strong></p>
5653.
5654
5655~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5656src line: 7078
5657
5658.
5659__ is not an empty emphasis
5660.
5661<p>__ is not an empty emphasis</p>
5662.
5663
5664~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5665src line: 7085
5666
5667.
5668____ is not an empty strong emphasis
5669.
5670<p>____ is not an empty strong emphasis</p>
5671.
5672
5673~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5674src line: 7095
5675
5676.
5677foo ***
5678.
5679<p>foo ***</p>
5680.
5681
5682~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5683src line: 7102
5684
5685.
5686foo *\**
5687.
5688<p>foo <em>*</em></p>
5689.
5690
5691~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5692src line: 7109
5693
5694.
5695foo *_*
5696.
5697<p>foo <em>_</em></p>
5698.
5699
5700~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5701src line: 7116
5702
5703.
5704foo *****
5705.
5706<p>foo *****</p>
5707.
5708
5709~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5710src line: 7123
5711
5712.
5713foo **\***
5714.
5715<p>foo <strong>*</strong></p>
5716.
5717
5718~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5719src line: 7130
5720
5721.
5722foo **_**
5723.
5724<p>foo <strong>_</strong></p>
5725.
5726
5727~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5728src line: 7141
5729
5730.
5731**foo*
5732.
5733<p>*<em>foo</em></p>
5734.
5735
5736~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5737src line: 7148
5738
5739.
5740*foo**
5741.
5742<p><em>foo</em>*</p>
5743.
5744
5745~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5746src line: 7155
5747
5748.
5749***foo**
5750.
5751<p>*<strong>foo</strong></p>
5752.
5753
5754~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5755src line: 7162
5756
5757.
5758****foo*
5759.
5760<p>***<em>foo</em></p>
5761.
5762
5763~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5764src line: 7169
5765
5766.
5767**foo***
5768.
5769<p><strong>foo</strong>*</p>
5770.
5771
5772~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5773src line: 7176
5774
5775.
5776*foo****
5777.
5778<p><em>foo</em>***</p>
5779.
5780
5781~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5782src line: 7186
5783
5784.
5785foo ___
5786.
5787<p>foo ___</p>
5788.
5789
5790~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5791src line: 7193
5792
5793.
5794foo _\__
5795.
5796<p>foo <em>_</em></p>
5797.
5798
5799~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5800src line: 7200
5801
5802.
5803foo _*_
5804.
5805<p>foo <em>*</em></p>
5806.
5807
5808~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5809src line: 7207
5810
5811.
5812foo _____
5813.
5814<p>foo _____</p>
5815.
5816
5817~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5818src line: 7214
5819
5820.
5821foo __\___
5822.
5823<p>foo <strong>_</strong></p>
5824.
5825
5826~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5827src line: 7221
5828
5829.
5830foo __*__
5831.
5832<p>foo <strong>*</strong></p>
5833.
5834
5835~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5836src line: 7228
5837
5838.
5839__foo_
5840.
5841<p>_<em>foo</em></p>
5842.
5843
5844~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5845src line: 7239
5846
5847.
5848_foo__
5849.
5850<p><em>foo</em>_</p>
5851.
5852
5853~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5854src line: 7246
5855
5856.
5857___foo__
5858.
5859<p>_<strong>foo</strong></p>
5860.
5861
5862~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5863src line: 7253
5864
5865.
5866____foo_
5867.
5868<p>___<em>foo</em></p>
5869.
5870
5871~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5872src line: 7260
5873
5874.
5875__foo___
5876.
5877<p><strong>foo</strong>_</p>
5878.
5879
5880~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5881src line: 7267
5882
5883.
5884_foo____
5885.
5886<p><em>foo</em>___</p>
5887.
5888
5889~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5890src line: 7277
5891
5892.
5893**foo**
5894.
5895<p><strong>foo</strong></p>
5896.
5897
5898~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5899src line: 7284
5900
5901.
5902*_foo_*
5903.
5904<p><em><em>foo</em></em></p>
5905.
5906
5907~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5908src line: 7291
5909
5910.
5911__foo__
5912.
5913<p><strong>foo</strong></p>
5914.
5915
5916~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5917src line: 7298
5918
5919.
5920_*foo*_
5921.
5922<p><em><em>foo</em></em></p>
5923.
5924
5925~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5926src line: 7308
5927
5928.
5929****foo****
5930.
5931<p><strong><strong>foo</strong></strong></p>
5932.
5933
5934~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5935src line: 7315
5936
5937.
5938____foo____
5939.
5940<p><strong><strong>foo</strong></strong></p>
5941.
5942
5943~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5944src line: 7326
5945
5946.
5947******foo******
5948.
5949<p><strong><strong><strong>foo</strong></strong></strong></p>
5950.
5951
5952~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5953src line: 7335
5954
5955.
5956***foo***
5957.
5958<p><em><strong>foo</strong></em></p>
5959.
5960
5961~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5962src line: 7342
5963
5964.
5965_____foo_____
5966.
5967<p><em><strong><strong>foo</strong></strong></em></p>
5968.
5969
5970~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5971src line: 7351
5972
5973.
5974*foo _bar* baz_
5975.
5976<p><em>foo _bar</em> baz_</p>
5977.
5978
5979~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5980src line: 7358
5981
5982.
5983*foo __bar *baz bim__ bam*
5984.
5985<p><em>foo <strong>bar *baz bim</strong> bam</em></p>
5986.
5987
5988~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5989src line: 7367
5990
5991.
5992**foo **bar baz**
5993.
5994<p>**foo <strong>bar baz</strong></p>
5995.
5996
5997~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5998src line: 7374
5999
6000.
6001*foo *bar baz*
6002.
6003<p>*foo <em>bar baz</em></p>
6004.
6005
6006~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6007src line: 7383
6008
6009.
6010*[bar*](/url)
6011.
6012<p>*<a href="/url">bar*</a></p>
6013.
6014
6015~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6016src line: 7390
6017
6018.
6019_foo [bar_](/url)
6020.
6021<p>_foo <a href="/url">bar_</a></p>
6022.
6023
6024~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6025src line: 7397
6026
6027.
6028*<img src="foo" title="*"/>
6029.
6030<p>*<img src="foo" title="*"/></p>
6031.
6032
6033~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6034src line: 7404
6035
6036.
6037**<a href="**">
6038.
6039<p>**<a href="**"></p>
6040.
6041
6042~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6043src line: 7411
6044
6045.
6046__<a href="__">
6047.
6048<p>__<a href="__"></p>
6049.
6050
6051~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6052src line: 7418
6053
6054.
6055*a `*`*
6056.
6057<p><em>a <code>*</code></em></p>
6058.
6059
6060~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6061src line: 7425
6062
6063.
6064_a `_`_
6065.
6066<p><em>a <code>_</code></em></p>
6067.
6068
6069~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6070src line: 7432
6071
6072.
6073**a<http://foo.bar/?q=**>
6074.
6075<p>**a<a href="http://foo.bar/?q=**">http://foo.bar/?q=**</a></p>
6076.
6077
6078~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6079src line: 7439
6080
6081.
6082__a<http://foo.bar/?q=__>
6083.
6084<p>__a<a href="http://foo.bar/?q=__">http://foo.bar/?q=__</a></p>
6085.
6086
6087~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6088src line: 7527
6089
6090.
6091[link](/uri "title")
6092.
6093<p><a href="/uri" title="title">link</a></p>
6094.
6095
6096~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6097src line: 7537
6098
6099.
6100[link](/uri)
6101.
6102<p><a href="/uri">link</a></p>
6103.
6104
6105~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6106src line: 7543
6107
6108.
6109[](./target.md)
6110.
6111<p><a href="./target.md"></a></p>
6112.
6113
6114~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6115src line: 7550
6116
6117.
6118[link]()
6119.
6120<p><a href="">link</a></p>
6121.
6122
6123~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6124src line: 7557
6125
6126.
6127[link](<>)
6128.
6129<p><a href="">link</a></p>
6130.
6131
6132~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6133src line: 7564
6134
6135.
6136[]()
6137.
6138<p><a href=""></a></p>
6139.
6140
6141~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6142src line: 7573
6143
6144.
6145[link](/my uri)
6146.
6147<p>[link](/my uri)</p>
6148.
6149
6150~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6151src line: 7579
6152
6153.
6154[link](</my uri>)
6155.
6156<p><a href="/my%20uri">link</a></p>
6157.
6158
6159~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6160src line: 7588
6161
6162.
6163[link](foo
6164bar)
6165.
6166<p>[link](foo
6167bar)</p>
6168.
6169
6170~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6171src line: 7596
6172
6173.
6174[link](<foo
6175bar>)
6176.
6177<p>[link](<foo
6178bar>)</p>
6179.
6180
6181~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6182src line: 7607
6183
6184.
6185[a](<b)c>)
6186.
6187<p><a href="b)c">a</a></p>
6188.
6189
6190~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6191src line: 7615
6192
6193.
6194[link](<foo\>)
6195.
6196<p>[link](&lt;foo&gt;)</p>
6197.
6198
6199~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6200src line: 7624
6201
6202.
6203[a](<b)c
6204[a](<b)c>
6205[a](<b>c)
6206.
6207<p>[a](&lt;b)c
6208[a](&lt;b)c&gt;
6209[a](<b>c)</p>
6210.
6211
6212~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6213src line: 7636
6214
6215.
6216[link](\(foo\))
6217.
6218<p><a href="(foo)">link</a></p>
6219.
6220
6221~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6222src line: 7645
6223
6224.
6225[link](foo(and(bar)))
6226.
6227<p><a href="foo(and(bar))">link</a></p>
6228.
6229
6230~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6231src line: 7654
6232
6233.
6234[link](foo(and(bar))
6235.
6236<p>[link](foo(and(bar))</p>
6237.
6238
6239~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6240src line: 7661
6241
6242.
6243[link](foo\(and\(bar\))
6244.
6245<p><a href="foo(and(bar)">link</a></p>
6246.
6247
6248~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6249src line: 7668
6250
6251.
6252[link](<foo(and(bar)>)
6253.
6254<p><a href="foo(and(bar)">link</a></p>
6255.
6256
6257~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6258src line: 7678
6259
6260.
6261[link](foo\)\:)
6262.
6263<p><a href="foo):">link</a></p>
6264.
6265
6266~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6267src line: 7687
6268
6269.
6270[link](#fragment)
6271
6272[link](http://example.com#fragment)
6273
6274[link](http://example.com?foo=3#frag)
6275.
6276<p><a href="#fragment">link</a></p>
6277<p><a href="http://example.com#fragment">link</a></p>
6278<p><a href="http://example.com?foo=3#frag">link</a></p>
6279.
6280
6281~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6282src line: 7703
6283
6284.
6285[link](foo\bar)
6286.
6287<p><a href="foo%5Cbar">link</a></p>
6288.
6289
6290~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6291src line: 7719
6292
6293.
6294[link](foo%20b&auml;)
6295.
6296<p><a href="foo%20b%C3%A4">link</a></p>
6297.
6298
6299~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6300src line: 7730
6301
6302.
6303[link]("title")
6304.
6305<p><a href="%22title%22">link</a></p>
6306.
6307
6308~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6309src line: 7739
6310
6311.
6312[link](/url "title")
6313[link](/url 'title')
6314[link](/url (title))
6315.
6316<p><a href="/url" title="title">link</a>
6317<a href="/url" title="title">link</a>
6318<a href="/url" title="title">link</a></p>
6319.
6320
6321~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6322src line: 7753
6323
6324.
6325[link](/url "title \"&quot;")
6326.
6327<p><a href="/url" title="title &quot;&quot;">link</a></p>
6328.
6329
6330~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6331src line: 7764
6332
6333.
6334[link](/url "title")
6335.
6336<p><a href="/url%C2%A0%22title%22">link</a></p>
6337.
6338
6339~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6340src line: 7773
6341
6342.
6343[link](/url "title "and" title")
6344.
6345<p>[link](/url &quot;title &quot;and&quot; title&quot;)</p>
6346.
6347
6348~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6349src line: 7782
6350
6351.
6352[link](/url 'title "and" title')
6353.
6354<p><a href="/url" title="title &quot;and&quot; title">link</a></p>
6355.
6356
6357~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6358src line: 7807
6359
6360.
6361[link]( /uri
6362 "title" )
6363.
6364<p><a href="/uri" title="title">link</a></p>
6365.
6366
6367~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6368src line: 7818
6369
6370.
6371[link] (/uri)
6372.
6373<p>[link] (/uri)</p>
6374.
6375
6376~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6377src line: 7828
6378
6379.
6380[link [foo [bar]]](/uri)
6381.
6382<p><a href="/uri">link [foo [bar]]</a></p>
6383.
6384
6385~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6386src line: 7835
6387
6388.
6389[link] bar](/uri)
6390.
6391<p>[link] bar](/uri)</p>
6392.
6393
6394~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6395src line: 7842
6396
6397.
6398[link [bar](/uri)
6399.
6400<p>[link <a href="/uri">bar</a></p>
6401.
6402
6403~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6404src line: 7849
6405
6406.
6407[link \[bar](/uri)
6408.
6409<p><a href="/uri">link [bar</a></p>
6410.
6411
6412~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6413src line: 7858
6414
6415.
6416[link *foo **bar** `#`*](/uri)
6417.
6418<p><a href="/uri">link <em>foo <strong>bar</strong> <code>#</code></em></a></p>
6419.
6420
6421~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6422src line: 7865
6423
6424.
6425[![moon](moon.jpg)](/uri)
6426.
6427<p><a href="/uri"><img src="moon.jpg" alt="moon" /></a></p>
6428.
6429
6430~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6431src line: 7874
6432
6433.
6434[foo [bar](/uri)](/uri)
6435.
6436<p>[foo <a href="/uri">bar</a>](/uri)</p>
6437.
6438
6439~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6440src line: 7881
6441
6442.
6443[foo *[bar [baz](/uri)](/uri)*](/uri)
6444.
6445<p>[foo <em>[bar <a href="/uri">baz</a>](/uri)</em>](/uri)</p>
6446.
6447
6448~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6449src line: 7888
6450
6451.
6452![[[foo](uri1)](uri2)](uri3)
6453.
6454<p><img src="uri3" alt="[foo](uri2)" /></p>
6455.
6456
6457~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6458src line: 7898
6459
6460.
6461*[foo*](/uri)
6462.
6463<p>*<a href="/uri">foo*</a></p>
6464.
6465
6466~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6467src line: 7905
6468
6469.
6470[foo *bar](baz*)
6471.
6472<p><a href="baz*">foo *bar</a></p>
6473.
6474
6475~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6476src line: 7915
6477
6478.
6479*foo [bar* baz]
6480.
6481<p><em>foo [bar</em> baz]</p>
6482.
6483
6484~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6485src line: 7925
6486
6487.
6488[foo <bar attr="](baz)">
6489.
6490<p>[foo <bar attr="](baz)"></p>
6491.
6492
6493~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6494src line: 7932
6495
6496.
6497[foo`](/uri)`
6498.
6499<p>[foo<code>](/uri)</code></p>
6500.
6501
6502~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6503src line: 7939
6504
6505.
6506[foo<http://example.com/?search=](uri)>
6507.
6508<p>[foo<a href="http://example.com/?search=%5D(uri)">http://example.com/?search=](uri)</a></p>
6509.
6510
6511~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6512src line: 7977
6513
6514.
6515[foo][bar]
6516
6517[bar]: /url "title"
6518.
6519<p><a href="/url" title="title">foo</a></p>
6520.
6521
6522~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6523src line: 7992
6524
6525.
6526[link [foo [bar]]][ref]
6527
6528[ref]: /uri
6529.
6530<p><a href="/uri">link [foo [bar]]</a></p>
6531.
6532
6533~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6534src line: 8001
6535
6536.
6537[link \[bar][ref]
6538
6539[ref]: /uri
6540.
6541<p><a href="/uri">link [bar</a></p>
6542.
6543
6544~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6545src line: 8012
6546
6547.
6548[link *foo **bar** `#`*][ref]
6549
6550[ref]: /uri
6551.
6552<p><a href="/uri">link <em>foo <strong>bar</strong> <code>#</code></em></a></p>
6553.
6554
6555~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6556src line: 8021
6557
6558.
6559[![moon](moon.jpg)][ref]
6560
6561[ref]: /uri
6562.
6563<p><a href="/uri"><img src="moon.jpg" alt="moon" /></a></p>
6564.
6565
6566~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6567src line: 8032
6568
6569.
6570[foo [bar](/uri)][ref]
6571
6572[ref]: /uri
6573.
6574<p>[foo <a href="/uri">bar</a>]<a href="/uri">ref</a></p>
6575.
6576
6577~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6578src line: 8041
6579
6580.
6581[foo *bar [baz][ref]*][ref]
6582
6583[ref]: /uri
6584.
6585<p>[foo <em>bar <a href="/uri">baz</a></em>]<a href="/uri">ref</a></p>
6586.
6587
6588~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6589src line: 8056
6590
6591.
6592*[foo*][ref]
6593
6594[ref]: /uri
6595.
6596<p>*<a href="/uri">foo*</a></p>
6597.
6598
6599~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6600src line: 8065
6601
6602.
6603[foo *bar][ref]*
6604
6605[ref]: /uri
6606.
6607<p><a href="/uri">foo *bar</a>*</p>
6608.
6609
6610~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6611src line: 8077
6612
6613.
6614[foo <bar attr="][ref]">
6615
6616[ref]: /uri
6617.
6618<p>[foo <bar attr="][ref]"></p>
6619.
6620
6621~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6622src line: 8086
6623
6624.
6625[foo`][ref]`
6626
6627[ref]: /uri
6628.
6629<p>[foo<code>][ref]</code></p>
6630.
6631
6632~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6633src line: 8095
6634
6635.
6636[foo<http://example.com/?search=][ref]>
6637
6638[ref]: /uri
6639.
6640<p>[foo<a href="http://example.com/?search=%5D%5Bref%5D">http://example.com/?search=][ref]</a></p>
6641.
6642
6643~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6644src line: 8106
6645
6646.
6647[foo][BaR]
6648
6649[bar]: /url "title"
6650.
6651<p><a href="/url" title="title">foo</a></p>
6652.
6653
6654~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6655src line: 8117
6656
6657.
6658[ẞ]
6659
6660[SS]: /url
6661.
6662<p><a href="/url">ẞ</a></p>
6663.
6664
6665~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6666src line: 8129
6667
6668.
6669[Foo
6670 bar]: /url
6671
6672[Baz][Foo bar]
6673.
6674<p><a href="/url">Baz</a></p>
6675.
6676
6677~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6678src line: 8142
6679
6680.
6681[foo] [bar]
6682
6683[bar]: /url "title"
6684.
6685<p>[foo] <a href="/url" title="title">bar</a></p>
6686.
6687
6688~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6689src line: 8151
6690
6691.
6692[foo]
6693[bar]
6694
6695[bar]: /url "title"
6696.
6697<p>[foo]
6698<a href="/url" title="title">bar</a></p>
6699.
6700
6701~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6702src line: 8192
6703
6704.
6705[foo]: /url1
6706
6707[foo]: /url2
6708
6709[bar][foo]
6710.
6711<p><a href="/url1">bar</a></p>
6712.
6713
6714~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6715src line: 8207
6716
6717.
6718[bar][foo\!]
6719
6720[foo!]: /url
6721.
6722<p>[bar][foo!]</p>
6723.
6724
6725~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6726src line: 8219
6727
6728.
6729[foo][ref[]
6730
6731[ref[]: /uri
6732.
6733<p>[foo][ref[]</p>
6734<p>[ref[]: /uri</p>
6735.
6736
6737~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6738src line: 8229
6739
6740.
6741[foo][ref[bar]]
6742
6743[ref[bar]]: /uri
6744.
6745<p>[foo][ref[bar]]</p>
6746<p>[ref[bar]]: /uri</p>
6747.
6748
6749~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6750src line: 8239
6751
6752.
6753[[[foo]]]
6754
6755[[[foo]]]: /url
6756.
6757<p>[[[foo]]]</p>
6758<p>[[[foo]]]: /url</p>
6759.
6760
6761~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6762src line: 8249
6763
6764.
6765[foo][ref\[]
6766
6767[ref\[]: /uri
6768.
6769<p><a href="/uri">foo</a></p>
6770.
6771
6772~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6773src line: 8260
6774
6775.
6776[bar\\]: /uri
6777
6778[bar\\]
6779.
6780<p><a href="/uri">bar\</a></p>
6781.
6782
6783~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6784src line: 8272
6785
6786.
6787[]
6788
6789[]: /uri
6790.
6791<p>[]</p>
6792<p>[]: /uri</p>
6793.
6794
6795~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6796src line: 8282
6797
6798.
6799[
6800 ]
6801
6802[
6803 ]: /uri
6804.
6805<p>[
6806]</p>
6807<p>[
6808]: /uri</p>
6809.
6810
6811~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6812src line: 8305
6813
6814.
6815[foo][]
6816
6817[foo]: /url "title"
6818.
6819<p><a href="/url" title="title">foo</a></p>
6820.
6821
6822~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6823src line: 8314
6824
6825.
6826[*foo* bar][]
6827
6828[*foo* bar]: /url "title"
6829.
6830<p><a href="/url" title="title"><em>foo</em> bar</a></p>
6831.
6832
6833~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6834src line: 8325
6835
6836.
6837[Foo][]
6838
6839[foo]: /url "title"
6840.
6841<p><a href="/url" title="title">Foo</a></p>
6842.
6843
6844~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6845src line: 8338
6846
6847.
6848[foo]
6849[]
6850
6851[foo]: /url "title"
6852.
6853<p><a href="/url" title="title">foo</a>
6854[]</p>
6855.
6856
6857~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6858src line: 8358
6859
6860.
6861[foo]
6862
6863[foo]: /url "title"
6864.
6865<p><a href="/url" title="title">foo</a></p>
6866.
6867
6868~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6869src line: 8367
6870
6871.
6872[*foo* bar]
6873
6874[*foo* bar]: /url "title"
6875.
6876<p><a href="/url" title="title"><em>foo</em> bar</a></p>
6877.
6878
6879~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6880src line: 8376
6881
6882.
6883[[*foo* bar]]
6884
6885[*foo* bar]: /url "title"
6886.
6887<p>[<a href="/url" title="title"><em>foo</em> bar</a>]</p>
6888.
6889
6890~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6891src line: 8385
6892
6893.
6894[[bar [foo]
6895
6896[foo]: /url
6897.
6898<p>[[bar <a href="/url">foo</a></p>
6899.
6900
6901~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6902src line: 8396
6903
6904.
6905[Foo]
6906
6907[foo]: /url "title"
6908.
6909<p><a href="/url" title="title">Foo</a></p>
6910.
6911
6912~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6913src line: 8407
6914
6915.
6916[foo] bar
6917
6918[foo]: /url
6919.
6920<p><a href="/url">foo</a> bar</p>
6921.
6922
6923~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6924src line: 8419
6925
6926.
6927\[foo]
6928
6929[foo]: /url "title"
6930.
6931<p>[foo]</p>
6932.
6933
6934~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6935src line: 8431
6936
6937.
6938[foo*]: /url
6939
6940*[foo*]
6941.
6942<p>*<a href="/url">foo*</a></p>
6943.
6944
6945~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6946src line: 8443
6947
6948.
6949[foo][bar]
6950
6951[foo]: /url1
6952[bar]: /url2
6953.
6954<p><a href="/url2">foo</a></p>
6955.
6956
6957~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6958src line: 8452
6959
6960.
6961[foo][]
6962
6963[foo]: /url1
6964.
6965<p><a href="/url1">foo</a></p>
6966.
6967
6968~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6969src line: 8462
6970
6971.
6972[foo]()
6973
6974[foo]: /url1
6975.
6976<p><a href="">foo</a></p>
6977.
6978
6979~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6980src line: 8470
6981
6982.
6983[foo](not a link)
6984
6985[foo]: /url1
6986.
6987<p><a href="/url1">foo</a>(not a link)</p>
6988.
6989
6990~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6991src line: 8481
6992
6993.
6994[foo][bar][baz]
6995
6996[baz]: /url
6997.
6998<p>[foo]<a href="/url">bar</a></p>
6999.
7000
7001~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7002src line: 8493
7003
7004.
7005[foo][bar][baz]
7006
7007[baz]: /url1
7008[bar]: /url2
7009.
7010<p><a href="/url2">foo</a><a href="/url1">baz</a></p>
7011.
7012
7013~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7014src line: 8506
7015
7016.
7017[foo][bar][baz]
7018
7019[baz]: /url1
7020[foo]: /url2
7021.
7022<p>[foo]<a href="/url1">bar</a></p>
7023.
7024
7025~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7026src line: 8529
7027
7028.
7029![foo](/url "title")
7030.
7031<p><img src="/url" alt="foo" title="title" /></p>
7032.
7033
7034~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7035src line: 8536
7036
7037.
7038![foo *bar*]
7039
7040[foo *bar*]: train.jpg "train & tracks"
7041.
7042<p><img src="train.jpg" alt="foo bar" title="train &amp; tracks" /></p>
7043.
7044
7045~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7046src line: 8545
7047
7048.
7049![foo ![bar](/url)](/url2)
7050.
7051<p><img src="/url2" alt="foo bar" /></p>
7052.
7053
7054~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7055src line: 8552
7056
7057.
7058![foo [bar](/url)](/url2)
7059.
7060<p><img src="/url2" alt="foo bar" /></p>
7061.
7062
7063~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7064src line: 8566
7065
7066.
7067![foo *bar*][]
7068
7069[foo *bar*]: train.jpg "train & tracks"
7070.
7071<p><img src="train.jpg" alt="foo bar" title="train &amp; tracks" /></p>
7072.
7073
7074~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7075src line: 8575
7076
7077.
7078![foo *bar*][foobar]
7079
7080[FOOBAR]: train.jpg "train & tracks"
7081.
7082<p><img src="train.jpg" alt="foo bar" title="train &amp; tracks" /></p>
7083.
7084
7085~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7086src line: 8584
7087
7088.
7089![foo](train.jpg)
7090.
7091<p><img src="train.jpg" alt="foo" /></p>
7092.
7093
7094~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7095src line: 8591
7096
7097.
7098My ![foo bar](/path/to/train.jpg "title" )
7099.
7100<p>My <img src="/path/to/train.jpg" alt="foo bar" title="title" /></p>
7101.
7102
7103~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7104src line: 8598
7105
7106.
7107![foo](<url>)
7108.
7109<p><img src="url" alt="foo" /></p>
7110.
7111
7112~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7113src line: 8605
7114
7115.
7116![](/url)
7117.
7118<p><img src="/url" alt="" /></p>
7119.
7120
7121~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7122src line: 8614
7123
7124.
7125![foo][bar]
7126
7127[bar]: /url
7128.
7129<p><img src="/url" alt="foo" /></p>
7130.
7131
7132~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7133src line: 8623
7134
7135.
7136![foo][bar]
7137
7138[BAR]: /url
7139.
7140<p><img src="/url" alt="foo" /></p>
7141.
7142
7143~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7144src line: 8634
7145
7146.
7147![foo][]
7148
7149[foo]: /url "title"
7150.
7151<p><img src="/url" alt="foo" title="title" /></p>
7152.
7153
7154~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7155src line: 8643
7156
7157.
7158![*foo* bar][]
7159
7160[*foo* bar]: /url "title"
7161.
7162<p><img src="/url" alt="foo bar" title="title" /></p>
7163.
7164
7165~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7166src line: 8654
7167
7168.
7169![Foo][]
7170
7171[foo]: /url "title"
7172.
7173<p><img src="/url" alt="Foo" title="title" /></p>
7174.
7175
7176~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7177src line: 8666
7178
7179.
7180![foo]
7181[]
7182
7183[foo]: /url "title"
7184.
7185<p><img src="/url" alt="foo" title="title" />
7186[]</p>
7187.
7188
7189~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7190src line: 8679
7191
7192.
7193![foo]
7194
7195[foo]: /url "title"
7196.
7197<p><img src="/url" alt="foo" title="title" /></p>
7198.
7199
7200~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7201src line: 8688
7202
7203.
7204![*foo* bar]
7205
7206[*foo* bar]: /url "title"
7207.
7208<p><img src="/url" alt="foo bar" title="title" /></p>
7209.
7210
7211~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7212src line: 8699
7213
7214.
7215![[foo]]
7216
7217[[foo]]: /url "title"
7218.
7219<p>![[foo]]</p>
7220<p>[[foo]]: /url &quot;title&quot;</p>
7221.
7222
7223~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7224src line: 8711
7225
7226.
7227![Foo]
7228
7229[foo]: /url "title"
7230.
7231<p><img src="/url" alt="Foo" title="title" /></p>
7232.
7233
7234~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7235src line: 8723
7236
7237.
7238!\[foo]
7239
7240[foo]: /url "title"
7241.
7242<p>![foo]</p>
7243.
7244
7245~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7246src line: 8735
7247
7248.
7249\![foo]
7250
7251[foo]: /url "title"
7252.
7253<p>!<a href="/url" title="title">foo</a></p>
7254.
7255
7256~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7257src line: 8768
7258
7259.
7260<http://foo.bar.baz>
7261.
7262<p><a href="http://foo.bar.baz">http://foo.bar.baz</a></p>
7263.
7264
7265~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7266src line: 8775
7267
7268.
7269<http://foo.bar.baz/test?q=hello&id=22&boolean>
7270.
7271<p><a href="http://foo.bar.baz/test?q=hello&amp;id=22&amp;boolean">http://foo.bar.baz/test?q=hello&amp;id=22&amp;boolean</a></p>
7272.
7273
7274~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7275src line: 8782
7276
7277.
7278<irc://foo.bar:2233/baz>
7279.
7280<p><a href="irc://foo.bar:2233/baz">irc://foo.bar:2233/baz</a></p>
7281.
7282
7283~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7284src line: 8791
7285
7286.
7287<MAILTO:FOO@BAR.BAZ>
7288.
7289<p><a href="MAILTO:FOO@BAR.BAZ">MAILTO:FOO@BAR.BAZ</a></p>
7290.
7291
7292~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7293src line: 8803
7294
7295.
7296<a+b+c:d>
7297.
7298<p><a href="a+b+c:d">a+b+c:d</a></p>
7299.
7300
7301~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7302src line: 8810
7303
7304.
7305<made-up-scheme://foo,bar>
7306.
7307<p><a href="made-up-scheme://foo,bar">made-up-scheme://foo,bar</a></p>
7308.
7309
7310~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7311src line: 8817
7312
7313.
7314<http://../>
7315.
7316<p><a href="http://../">http://../</a></p>
7317.
7318
7319~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7320src line: 8824
7321
7322.
7323<localhost:5001/foo>
7324.
7325<p><a href="localhost:5001/foo">localhost:5001/foo</a></p>
7326.
7327
7328~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7329src line: 8833
7330
7331.
7332<http://foo.bar/baz bim>
7333.
7334<p>&lt;http://foo.bar/baz bim&gt;</p>
7335.
7336
7337~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7338src line: 8842
7339
7340.
7341<http://example.com/\[\>
7342.
7343<p><a href="http://example.com/%5C%5B%5C">http://example.com/\[\</a></p>
7344.
7345
7346~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7347src line: 8864
7348
7349.
7350<foo@bar.example.com>
7351.
7352<p><a href="mailto:foo@bar.example.com">foo@bar.example.com</a></p>
7353.
7354
7355~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7356src line: 8871
7357
7358.
7359<foo+special@Bar.baz-bar0.com>
7360.
7361<p><a href="mailto:foo+special@Bar.baz-bar0.com">foo+special@Bar.baz-bar0.com</a></p>
7362.
7363
7364~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7365src line: 8880
7366
7367.
7368<foo\+@bar.example.com>
7369.
7370<p>&lt;foo+@bar.example.com&gt;</p>
7371.
7372
7373~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7374src line: 8889
7375
7376.
7377<>
7378.
7379<p>&lt;&gt;</p>
7380.
7381
7382~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7383src line: 8896
7384
7385.
7386< http://foo.bar >
7387.
7388<p>&lt; http://foo.bar &gt;</p>
7389.
7390
7391~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7392src line: 8903
7393
7394.
7395<m:abc>
7396.
7397<p>&lt;m:abc&gt;</p>
7398.
7399
7400~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7401src line: 8910
7402
7403.
7404<foo.bar.baz>
7405.
7406<p>&lt;foo.bar.baz&gt;</p>
7407.
7408
7409~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7410src line: 8917
7411
7412.
7413http://example.com
7414.
7415<p>http://example.com</p>
7416.
7417
7418~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7419src line: 8924
7420
7421.
7422foo@bar.example.com
7423.
7424<p>foo@bar.example.com</p>
7425.
7426
7427~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7428src line: 9005
7429
7430.
7431<a><bab><c2c>
7432.
7433<p><a><bab><c2c></p>
7434.
7435
7436~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7437src line: 9014
7438
7439.
7440<a/><b2/>
7441.
7442<p><a/><b2/></p>
7443.
7444
7445~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7446src line: 9023
7447
7448.
7449<a /><b2
7450data="foo" >
7451.
7452<p><a /><b2
7453data="foo" ></p>
7454.
7455
7456~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7457src line: 9034
7458
7459.
7460<a foo="bar" bam = 'baz <em>"</em>'
7461_boolean zoop:33=zoop:33 />
7462.
7463<p><a foo="bar" bam = 'baz <em>"</em>'
7464_boolean zoop:33=zoop:33 /></p>
7465.
7466
7467~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7468src line: 9045
7469
7470.
7471Foo <responsive-image src="foo.jpg" />
7472.
7473<p>Foo <responsive-image src="foo.jpg" /></p>
7474.
7475
7476~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7477src line: 9054
7478
7479.
7480<33> <__>
7481.
7482<p>&lt;33&gt; &lt;__&gt;</p>
7483.
7484
7485~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7486src line: 9063
7487
7488.
7489<a h*#ref="hi">
7490.
7491<p>&lt;a h*#ref=&quot;hi&quot;&gt;</p>
7492.
7493
7494~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7495src line: 9072
7496
7497.
7498<a href="hi'> <a href=hi'>
7499.
7500<p>&lt;a href=&quot;hi'&gt; &lt;a href=hi'&gt;</p>
7501.
7502
7503~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7504src line: 9081
7505
7506.
7507< a><
7508foo><bar/ >
7509<foo bar=baz
7510bim!bop />
7511.
7512<p>&lt; a&gt;&lt;
7513foo&gt;&lt;bar/ &gt;
7514&lt;foo bar=baz
7515bim!bop /&gt;</p>
7516.
7517
7518~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7519src line: 9096
7520
7521.
7522<a href='bar'title=title>
7523.
7524<p>&lt;a href='bar'title=title&gt;</p>
7525.
7526
7527~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7528src line: 9105
7529
7530.
7531</a></foo >
7532.
7533<p></a></foo ></p>
7534.
7535
7536~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7537src line: 9114
7538
7539.
7540</a href="foo">
7541.
7542<p>&lt;/a href=&quot;foo&quot;&gt;</p>
7543.
7544
7545~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7546src line: 9123
7547
7548.
7549foo <!-- this is a
7550comment - with hyphen -->
7551.
7552<p>foo <!-- this is a
7553comment - with hyphen --></p>
7554.
7555
7556~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7557src line: 9132
7558
7559.
7560foo <!-- not a comment -- two hyphens -->
7561.
7562<p>foo &lt;!-- not a comment -- two hyphens --&gt;</p>
7563.
7564
7565~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7566src line: 9141
7567
7568.
7569foo <!--> foo -->
7570
7571foo <!-- foo--->
7572.
7573<p>foo &lt;!--&gt; foo --&gt;</p>
7574<p>foo &lt;!-- foo---&gt;</p>
7575.
7576
7577~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7578src line: 9153
7579
7580.
7581foo <?php echo $a; ?>
7582.
7583<p>foo <?php echo $a; ?></p>
7584.
7585
7586~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7587src line: 9162
7588
7589.
7590foo <!ELEMENT br EMPTY>
7591.
7592<p>foo <!ELEMENT br EMPTY></p>
7593.
7594
7595~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7596src line: 9171
7597
7598.
7599foo <![CDATA[>&<]]>
7600.
7601<p>foo <![CDATA[>&<]]></p>
7602.
7603
7604~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7605src line: 9181
7606
7607.
7608foo <a href="&ouml;">
7609.
7610<p>foo <a href="&ouml;"></p>
7611.
7612
7613~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7614src line: 9190
7615
7616.
7617foo <a href="\*">
7618.
7619<p>foo <a href="\*"></p>
7620.
7621
7622~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7623src line: 9197
7624
7625.
7626<a href="\"">
7627.
7628<p>&lt;a href=&quot;&quot;&quot;&gt;</p>
7629.
7630
7631~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7632src line: 9211
7633
7634.
7635foo
7636baz
7637.
7638<p>foo<br />
7639baz</p>
7640.
7641
7642~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7643src line: 9223
7644
7645.
7646foo\
7647baz
7648.
7649<p>foo<br />
7650baz</p>
7651.
7652
7653~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7654src line: 9234
7655
7656.
7657foo
7658baz
7659.
7660<p>foo<br />
7661baz</p>
7662.
7663
7664~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7665src line: 9245
7666
7667.
7668foo
7669 bar
7670.
7671<p>foo<br />
7672bar</p>
7673.
7674
7675~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7676src line: 9254
7677
7678.
7679foo\
7680 bar
7681.
7682<p>foo<br />
7683bar</p>
7684.
7685
7686~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7687src line: 9266
7688
7689.
7690*foo
7691bar*
7692.
7693<p><em>foo<br />
7694bar</em></p>
7695.
7696
7697~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7698src line: 9275
7699
7700.
7701*foo\
7702bar*
7703.
7704<p><em>foo<br />
7705bar</em></p>
7706.
7707
7708~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7709src line: 9286
7710
7711.
7712`code
7713span`
7714.
7715<p><code>code span</code></p>
7716.
7717
7718~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7719src line: 9294
7720
7721.
7722`code\
7723span`
7724.
7725<p><code>code\ span</code></p>
7726.
7727
7728~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7729src line: 9304
7730
7731.
7732<a href="foo
7733bar">
7734.
7735<p><a href="foo
7736bar"></p>
7737.
7738
7739~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7740src line: 9313
7741
7742.
7743<a href="foo\
7744bar">
7745.
7746<p><a href="foo\
7747bar"></p>
7748.
7749
7750~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7751src line: 9326
7752
7753.
7754foo\
7755.
7756<p>foo\</p>
7757.
7758
7759~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7760src line: 9333
7761
7762.
7763foo
7764.
7765<p>foo</p>
7766.
7767
7768~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7769src line: 9340
7770
7771.
7772### foo\
7773.
7774<h3>foo\</h3>
7775.
7776
7777~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7778src line: 9347
7779
7780.
7781### foo
7782.
7783<h3>foo</h3>
7784.
7785
7786~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7787src line: 9362
7788
7789.
7790foo
7791baz
7792.
7793<p>foo
7794baz</p>
7795.
7796
7797~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7798src line: 9374
7799
7800.
7801foo
7802 baz
7803.
7804<p>foo
7805baz</p>
7806.
7807
7808~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7809src line: 9394
7810
7811.
7812hello $.;'there
7813.
7814<p>hello $.;'there</p>
7815.
7816
7817~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7818src line: 9401
7819
7820.
7821Foo χρῆν
7822.
7823<p>Foo χρῆν</p>
7824.
7825
7826~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7827src line: 9410
7828
7829.
7830Multiple spaces
7831.
7832<p>Multiple spaces</p>
7833.
7834
crates/markdown-it/tests/fixtures/commonmark/spec.txt created+9756
...@@ -0,0 +1,9756 @@
1---
2title: CommonMark Spec
3author: John MacFarlane
4version: 0.30
5date: '2021-06-19'
6license: '[CC-BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/)'
7...
8
9# Introduction
10
11## What is Markdown?
12
13Markdown is a plain text format for writing structured documents,
14based on conventions for indicating formatting in email
15and usenet posts. It was developed by John Gruber (with
16help from Aaron Swartz) and released in 2004 in the form of a
17[syntax description](http://daringfireball.net/projects/markdown/syntax)
18and a Perl script (`Markdown.pl`) for converting Markdown to
19HTML. In the next decade, dozens of implementations were
20developed in many languages. Some extended the original
21Markdown syntax with conventions for footnotes, tables, and
22other document elements. Some allowed Markdown documents to be
23rendered in formats other than HTML. Websites like Reddit,
24StackOverflow, and GitHub had millions of people using Markdown.
25And Markdown started to be used beyond the web, to author books,
26articles, slide shows, letters, and lecture notes.
27
28What distinguishes Markdown from many other lightweight markup
29syntaxes, which are often easier to write, is its readability.
30As Gruber writes:
31
32> The overriding design goal for Markdown's formatting syntax is
33> to make it as readable as possible. The idea is that a
34> Markdown-formatted document should be publishable as-is, as
35> plain text, without looking like it's been marked up with tags
36> or formatting instructions.
37> (<http://daringfireball.net/projects/markdown/>)
38
39The point can be illustrated by comparing a sample of
40[AsciiDoc](http://www.methods.co.nz/asciidoc/) with
41an equivalent sample of Markdown. Here is a sample of
42AsciiDoc from the AsciiDoc manual:
43
44```
451. List item one.
46+
47List item one continued with a second paragraph followed by an
48Indented block.
49+
50.................
51$ ls *.sh
52$ mv *.sh ~/tmp
53.................
54+
55List item continued with a third paragraph.
56
572. List item two continued with an open block.
58+
59--
60This paragraph is part of the preceding list item.
61
62a. This list is nested and does not require explicit item
63continuation.
64+
65This paragraph is part of the preceding list item.
66
67b. List item b.
68
69This paragraph belongs to item two of the outer list.
70--
71```
72
73And here is the equivalent in Markdown:
74```
751. List item one.
76
77 List item one continued with a second paragraph followed by an
78 Indented block.
79
80 $ ls *.sh
81 $ mv *.sh ~/tmp
82
83 List item continued with a third paragraph.
84
852. List item two continued with an open block.
86
87 This paragraph is part of the preceding list item.
88
89 1. This list is nested and does not require explicit item continuation.
90
91 This paragraph is part of the preceding list item.
92
93 2. List item b.
94
95 This paragraph belongs to item two of the outer list.
96```
97
98The AsciiDoc version is, arguably, easier to write. You don't need
99to worry about indentation. But the Markdown version is much easier
100to read. The nesting of list items is apparent to the eye in the
101source, not just in the processed document.
102
103## Why is a spec needed?
104
105John Gruber's [canonical description of Markdown's
106syntax](http://daringfireball.net/projects/markdown/syntax)
107does not specify the syntax unambiguously. Here are some examples of
108questions it does not answer:
109
1101. How much indentation is needed for a sublist? The spec says that
111 continuation paragraphs need to be indented four spaces, but is
112 not fully explicit about sublists. It is natural to think that
113 they, too, must be indented four spaces, but `Markdown.pl` does
114 not require that. This is hardly a "corner case," and divergences
115 between implementations on this issue often lead to surprises for
116 users in real documents. (See [this comment by John
117 Gruber](http://article.gmane.org/gmane.text.markdown.general/1997).)
118
1192. Is a blank line needed before a block quote or heading?
120 Most implementations do not require the blank line. However,
121 this can lead to unexpected results in hard-wrapped text, and
122 also to ambiguities in parsing (note that some implementations
123 put the heading inside the blockquote, while others do not).
124 (John Gruber has also spoken [in favor of requiring the blank
125 lines](http://article.gmane.org/gmane.text.markdown.general/2146).)
126
1273. Is a blank line needed before an indented code block?
128 (`Markdown.pl` requires it, but this is not mentioned in the
129 documentation, and some implementations do not require it.)
130
131 ``` markdown
132 paragraph
133 code?
134 ```
135
1364. What is the exact rule for determining when list items get
137 wrapped in `<p>` tags? Can a list be partially "loose" and partially
138 "tight"? What should we do with a list like this?
139
140 ``` markdown
141 1. one
142
143 2. two
144 3. three
145 ```
146
147 Or this?
148
149 ``` markdown
150 1. one
151 - a
152
153 - b
154 2. two
155 ```
156
157 (There are some relevant comments by John Gruber
158 [here](http://article.gmane.org/gmane.text.markdown.general/2554).)
159
1605. Can list markers be indented? Can ordered list markers be right-aligned?
161
162 ``` markdown
163 8. item 1
164 9. item 2
165 10. item 2a
166 ```
167
1686. Is this one list with a thematic break in its second item,
169 or two lists separated by a thematic break?
170
171 ``` markdown
172 * a
173 * * * * *
174 * b
175 ```
176
1777. When list markers change from numbers to bullets, do we have
178 two lists or one? (The Markdown syntax description suggests two,
179 but the perl scripts and many other implementations produce one.)
180
181 ``` markdown
182 1. fee
183 2. fie
184 - foe
185 - fum
186 ```
187
1888. What are the precedence rules for the markers of inline structure?
189 For example, is the following a valid link, or does the code span
190 take precedence ?
191
192 ``` markdown
193 [a backtick (`)](/url) and [another backtick (`)](/url).
194 ```
195
1969. What are the precedence rules for markers of emphasis and strong
197 emphasis? For example, how should the following be parsed?
198
199 ``` markdown
200 *foo *bar* baz*
201 ```
202
20310. What are the precedence rules between block-level and inline-level
204 structure? For example, how should the following be parsed?
205
206 ``` markdown
207 - `a long code span can contain a hyphen like this
208 - and it can screw things up`
209 ```
210
21111. Can list items include section headings? (`Markdown.pl` does not
212 allow this, but does allow blockquotes to include headings.)
213
214 ``` markdown
215 - # Heading
216 ```
217
21812. Can list items be empty?
219
220 ``` markdown
221 * a
222 *
223 * b
224 ```
225
22613. Can link references be defined inside block quotes or list items?
227
228 ``` markdown
229 > Blockquote [foo].
230 >
231 > [foo]: /url
232 ```
233
23414. If there are multiple definitions for the same reference, which takes
235 precedence?
236
237 ``` markdown
238 [foo]: /url1
239 [foo]: /url2
240
241 [foo][]
242 ```
243
244In the absence of a spec, early implementers consulted `Markdown.pl`
245to resolve these ambiguities. But `Markdown.pl` was quite buggy, and
246gave manifestly bad results in many cases, so it was not a
247satisfactory replacement for a spec.
248
249Because there is no unambiguous spec, implementations have diverged
250considerably. As a result, users are often surprised to find that
251a document that renders one way on one system (say, a GitHub wiki)
252renders differently on another (say, converting to docbook using
253pandoc). To make matters worse, because nothing in Markdown counts
254as a "syntax error," the divergence often isn't discovered right away.
255
256## About this document
257
258This document attempts to specify Markdown syntax unambiguously.
259It contains many examples with side-by-side Markdown and
260HTML. These are intended to double as conformance tests. An
261accompanying script `spec_tests.py` can be used to run the tests
262against any Markdown program:
263
264 python test/spec_tests.py --spec spec.txt --program PROGRAM
265
266Since this document describes how Markdown is to be parsed into
267an abstract syntax tree, it would have made sense to use an abstract
268representation of the syntax tree instead of HTML. But HTML is capable
269of representing the structural distinctions we need to make, and the
270choice of HTML for the tests makes it possible to run the tests against
271an implementation without writing an abstract syntax tree renderer.
272
273Note that not every feature of the HTML samples is mandated by
274the spec. For example, the spec says what counts as a link
275destination, but it doesn't mandate that non-ASCII characters in
276the URL be percent-encoded. To use the automatic tests,
277implementers will need to provide a renderer that conforms to
278the expectations of the spec examples (percent-encoding
279non-ASCII characters in URLs). But a conforming implementation
280can use a different renderer and may choose not to
281percent-encode non-ASCII characters in URLs.
282
283This document is generated from a text file, `spec.txt`, written
284in Markdown with a small extension for the side-by-side tests.
285The script `tools/makespec.py` can be used to convert `spec.txt` into
286HTML or CommonMark (which can then be converted into other formats).
287
288In the examples, the `→` character is used to represent tabs.
289
290# Preliminaries
291
292## Characters and lines
293
294Any sequence of [characters] is a valid CommonMark
295document.
296
297A [character](@) is a Unicode code point. Although some
298code points (for example, combining accents) do not correspond to
299characters in an intuitive sense, all code points count as characters
300for purposes of this spec.
301
302This spec does not specify an encoding; it thinks of lines as composed
303of [characters] rather than bytes. A conforming parser may be limited
304to a certain encoding.
305
306A [line](@) is a sequence of zero or more [characters]
307other than line feed (`U+000A`) or carriage return (`U+000D`),
308followed by a [line ending] or by the end of file.
309
310A [line ending](@) is a line feed (`U+000A`), a carriage return
311(`U+000D`) not followed by a line feed, or a carriage return and a
312following line feed.
313
314A line containing no characters, or a line containing only spaces
315(`U+0020`) or tabs (`U+0009`), is called a [blank line](@).
316
317The following definitions of character classes will be used in this spec:
318
319A [Unicode whitespace character](@) is
320any code point in the Unicode `Zs` general category, or a tab (`U+0009`),
321line feed (`U+000A`), form feed (`U+000C`), or carriage return (`U+000D`).
322
323[Unicode whitespace](@) is a sequence of one or more
324[Unicode whitespace characters].
325
326A [tab](@) is `U+0009`.
327
328A [space](@) is `U+0020`.
329
330An [ASCII control character](@) is a character between `U+0000–1F` (both
331including) or `U+007F`.
332
333An [ASCII punctuation character](@)
334is `!`, `"`, `#`, `$`, `%`, `&`, `'`, `(`, `)`,
335`*`, `+`, `,`, `-`, `.`, `/` (U+0021–2F),
336`:`, `;`, `<`, `=`, `>`, `?`, `@` (U+003A–0040),
337`[`, `\`, `]`, `^`, `_`, `` ` `` (U+005B–0060),
338`{`, `|`, `}`, or `~` (U+007B–007E).
339
340A [Unicode punctuation character](@) is an [ASCII
341punctuation character] or anything in
342the general Unicode categories `Pc`, `Pd`, `Pe`, `Pf`, `Pi`, `Po`, or `Ps`.
343
344## Tabs
345
346Tabs in lines are not expanded to [spaces]. However,
347in contexts where spaces help to define block structure,
348tabs behave as if they were replaced by spaces with a tab stop
349of 4 characters.
350
351Thus, for example, a tab can be used instead of four spaces
352in an indented code block. (Note, however, that internal
353tabs are passed through as literal tabs, not expanded to
354spaces.)
355
356```````````````````````````````` example
357→foo→baz→→bim
358.
359<pre><code>foo→baz→→bim
360</code></pre>
361````````````````````````````````
362
363```````````````````````````````` example
364 →foo→baz→→bim
365.
366<pre><code>foo→baz→→bim
367</code></pre>
368````````````````````````````````
369
370```````````````````````````````` example
371 a→a
372 ὐ→a
373.
374<pre><code>a→a
375ὐ→a
376</code></pre>
377````````````````````````````````
378
379In the following example, a continuation paragraph of a list
380item is indented with a tab; this has exactly the same effect
381as indentation with four spaces would:
382
383```````````````````````````````` example
384 - foo
385
386→bar
387.
388<ul>
389<li>
390<p>foo</p>
391<p>bar</p>
392</li>
393</ul>
394````````````````````````````````
395
396```````````````````````````````` example
397- foo
398
399→→bar
400.
401<ul>
402<li>
403<p>foo</p>
404<pre><code> bar
405</code></pre>
406</li>
407</ul>
408````````````````````````````````
409
410Normally the `>` that begins a block quote may be followed
411optionally by a space, which is not considered part of the
412content. In the following case `>` is followed by a tab,
413which is treated as if it were expanded into three spaces.
414Since one of these spaces is considered part of the
415delimiter, `foo` is considered to be indented six spaces
416inside the block quote context, so we get an indented
417code block starting with two spaces.
418
419```````````````````````````````` example
420>→→foo
421.
422<blockquote>
423<pre><code> foo
424</code></pre>
425</blockquote>
426````````````````````````````````
427
428```````````````````````````````` example
429-→→foo
430.
431<ul>
432<li>
433<pre><code> foo
434</code></pre>
435</li>
436</ul>
437````````````````````````````````
438
439
440```````````````````````````````` example
441 foo
442→bar
443.
444<pre><code>foo
445bar
446</code></pre>
447````````````````````````````````
448
449```````````````````````````````` example
450 - foo
451 - bar
452→ - baz
453.
454<ul>
455<li>foo
456<ul>
457<li>bar
458<ul>
459<li>baz</li>
460</ul>
461</li>
462</ul>
463</li>
464</ul>
465````````````````````````````````
466
467```````````````````````````````` example
468#→Foo
469.
470<h1>Foo</h1>
471````````````````````````````````
472
473```````````````````````````````` example
474*→*→*→
475.
476<hr />
477````````````````````````````````
478
479
480## Insecure characters
481
482For security reasons, the Unicode character `U+0000` must be replaced
483with the REPLACEMENT CHARACTER (`U+FFFD`).
484
485
486## Backslash escapes
487
488Any ASCII punctuation character may be backslash-escaped:
489
490```````````````````````````````` example
491\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~
492.
493<p>!&quot;#$%&amp;'()*+,-./:;&lt;=&gt;?@[\]^_`{|}~</p>
494````````````````````````````````
495
496
497Backslashes before other characters are treated as literal
498backslashes:
499
500```````````````````````````````` example
501\→\A\a\ \3\φ\«
502.
503<p>\→\A\a\ \3\φ\«</p>
504````````````````````````````````
505
506
507Escaped characters are treated as regular characters and do
508not have their usual Markdown meanings:
509
510```````````````````````````````` example
511\*not emphasized*
512\<br/> not a tag
513\[not a link](/foo)
514\`not code`
5151\. not a list
516\* not a list
517\# not a heading
518\[foo]: /url "not a reference"
519\&ouml; not a character entity
520.
521<p>*not emphasized*
522&lt;br/&gt; not a tag
523[not a link](/foo)
524`not code`
5251. not a list
526* not a list
527# not a heading
528[foo]: /url &quot;not a reference&quot;
529&amp;ouml; not a character entity</p>
530````````````````````````````````
531
532
533If a backslash is itself escaped, the following character is not:
534
535```````````````````````````````` example
536\\*emphasis*
537.
538<p>\<em>emphasis</em></p>
539````````````````````````````````
540
541
542A backslash at the end of the line is a [hard line break]:
543
544```````````````````````````````` example
545foo\
546bar
547.
548<p>foo<br />
549bar</p>
550````````````````````````````````
551
552
553Backslash escapes do not work in code blocks, code spans, autolinks, or
554raw HTML:
555
556```````````````````````````````` example
557`` \[\` ``
558.
559<p><code>\[\`</code></p>
560````````````````````````````````
561
562
563```````````````````````````````` example
564 \[\]
565.
566<pre><code>\[\]
567</code></pre>
568````````````````````````````````
569
570
571```````````````````````````````` example
572~~~
573\[\]
574~~~
575.
576<pre><code>\[\]
577</code></pre>
578````````````````````````````````
579
580
581```````````````````````````````` example
582<http://example.com?find=\*>
583.
584<p><a href="http://example.com?find=%5C*">http://example.com?find=\*</a></p>
585````````````````````````````````
586
587
588```````````````````````````````` example
589<a href="/bar\/)">
590.
591<a href="/bar\/)">
592````````````````````````````````
593
594
595But they work in all other contexts, including URLs and link titles,
596link references, and [info strings] in [fenced code blocks]:
597
598```````````````````````````````` example
599[foo](/bar\* "ti\*tle")
600.
601<p><a href="/bar*" title="ti*tle">foo</a></p>
602````````````````````````````````
603
604
605```````````````````````````````` example
606[foo]
607
608[foo]: /bar\* "ti\*tle"
609.
610<p><a href="/bar*" title="ti*tle">foo</a></p>
611````````````````````````````````
612
613
614```````````````````````````````` example
615``` foo\+bar
616foo
617```
618.
619<pre><code class="language-foo+bar">foo
620</code></pre>
621````````````````````````````````
622
623
624## Entity and numeric character references
625
626Valid HTML entity references and numeric character references
627can be used in place of the corresponding Unicode character,
628with the following exceptions:
629
630- Entity and character references are not recognized in code
631 blocks and code spans.
632
633- Entity and character references cannot stand in place of
634 special characters that define structural elements in
635 CommonMark. For example, although `&#42;` can be used
636 in place of a literal `*` character, `&#42;` cannot replace
637 `*` in emphasis delimiters, bullet list markers, or thematic
638 breaks.
639
640Conforming CommonMark parsers need not store information about
641whether a particular character was represented in the source
642using a Unicode character or an entity reference.
643
644[Entity references](@) consist of `&` + any of the valid
645HTML5 entity names + `;`. The
646document <https://html.spec.whatwg.org/entities.json>
647is used as an authoritative source for the valid entity
648references and their corresponding code points.
649
650```````````````````````````````` example
651&nbsp; &amp; &copy; &AElig; &Dcaron;
652&frac34; &HilbertSpace; &DifferentialD;
653&ClockwiseContourIntegral; &ngE;
654.
655<p>  &amp; © Æ Ď
656¾ ℋ ⅆ
657∲ ≧̸</p>
658````````````````````````````````
659
660
661[Decimal numeric character
662references](@)
663consist of `&#` + a string of 1--7 arabic digits + `;`. A
664numeric character reference is parsed as the corresponding
665Unicode character. Invalid Unicode code points will be replaced by
666the REPLACEMENT CHARACTER (`U+FFFD`). For security reasons,
667the code point `U+0000` will also be replaced by `U+FFFD`.
668
669```````````````````````````````` example
670&#35; &#1234; &#992; &#0;
671.
672<p># Ӓ Ϡ �</p>
673````````````````````````````````
674
675
676[Hexadecimal numeric character
677references](@) consist of `&#` +
678either `X` or `x` + a string of 1-6 hexadecimal digits + `;`.
679They too are parsed as the corresponding Unicode character (this
680time specified with a hexadecimal numeral instead of decimal).
681
682```````````````````````````````` example
683&#X22; &#XD06; &#xcab;
684.
685<p>&quot; ആ ಫ</p>
686````````````````````````````````
687
688
689Here are some nonentities:
690
691```````````````````````````````` example
692&nbsp &x; &#; &#x;
693&#87654321;
694&#abcdef0;
695&ThisIsNotDefined; &hi?;
696.
697<p>&amp;nbsp &amp;x; &amp;#; &amp;#x;
698&amp;#87654321;
699&amp;#abcdef0;
700&amp;ThisIsNotDefined; &amp;hi?;</p>
701````````````````````````````````
702
703
704Although HTML5 does accept some entity references
705without a trailing semicolon (such as `&copy`), these are not
706recognized here, because it makes the grammar too ambiguous:
707
708```````````````````````````````` example
709&copy
710.
711<p>&amp;copy</p>
712````````````````````````````````
713
714
715Strings that are not on the list of HTML5 named entities are not
716recognized as entity references either:
717
718```````````````````````````````` example
719&MadeUpEntity;
720.
721<p>&amp;MadeUpEntity;</p>
722````````````````````````````````
723
724
725Entity and numeric character references are recognized in any
726context besides code spans or code blocks, including
727URLs, [link titles], and [fenced code block][] [info strings]:
728
729```````````````````````````````` example
730<a href="&ouml;&ouml;.html">
731.
732<a href="&ouml;&ouml;.html">
733````````````````````````````````
734
735
736```````````````````````````````` example
737[foo](/f&ouml;&ouml; "f&ouml;&ouml;")
738.
739<p><a href="/f%C3%B6%C3%B6" title="föö">foo</a></p>
740````````````````````````````````
741
742
743```````````````````````````````` example
744[foo]
745
746[foo]: /f&ouml;&ouml; "f&ouml;&ouml;"
747.
748<p><a href="/f%C3%B6%C3%B6" title="föö">foo</a></p>
749````````````````````````````````
750
751
752```````````````````````````````` example
753``` f&ouml;&ouml;
754foo
755```
756.
757<pre><code class="language-föö">foo
758</code></pre>
759````````````````````````````````
760
761
762Entity and numeric character references are treated as literal
763text in code spans and code blocks:
764
765```````````````````````````````` example
766`f&ouml;&ouml;`
767.
768<p><code>f&amp;ouml;&amp;ouml;</code></p>
769````````````````````````````````
770
771
772```````````````````````````````` example
773 f&ouml;f&ouml;
774.
775<pre><code>f&amp;ouml;f&amp;ouml;
776</code></pre>
777````````````````````````````````
778
779
780Entity and numeric character references cannot be used
781in place of symbols indicating structure in CommonMark
782documents.
783
784```````````````````````````````` example
785&#42;foo&#42;
786*foo*
787.
788<p>*foo*
789<em>foo</em></p>
790````````````````````````````````
791
792```````````````````````````````` example
793&#42; foo
794
795* foo
796.
797<p>* foo</p>
798<ul>
799<li>foo</li>
800</ul>
801````````````````````````````````
802
803```````````````````````````````` example
804foo&#10;&#10;bar
805.
806<p>foo
807
808bar</p>
809````````````````````````````````
810
811```````````````````````````````` example
812&#9;foo
813.
814<p>→foo</p>
815````````````````````````````````
816
817
818```````````````````````````````` example
819[a](url &quot;tit&quot;)
820.
821<p>[a](url &quot;tit&quot;)</p>
822````````````````````````````````
823
824
825
826# Blocks and inlines
827
828We can think of a document as a sequence of
829[blocks](@)---structural elements like paragraphs, block
830quotations, lists, headings, rules, and code blocks. Some blocks (like
831block quotes and list items) contain other blocks; others (like
832headings and paragraphs) contain [inline](@) content---text,
833links, emphasized text, images, code spans, and so on.
834
835## Precedence
836
837Indicators of block structure always take precedence over indicators
838of inline structure. So, for example, the following is a list with
839two items, not a list with one item containing a code span:
840
841```````````````````````````````` example
842- `one
843- two`
844.
845<ul>
846<li>`one</li>
847<li>two`</li>
848</ul>
849````````````````````````````````
850
851
852This means that parsing can proceed in two steps: first, the block
853structure of the document can be discerned; second, text lines inside
854paragraphs, headings, and other block constructs can be parsed for inline
855structure. The second step requires information about link reference
856definitions that will be available only at the end of the first
857step. Note that the first step requires processing lines in sequence,
858but the second can be parallelized, since the inline parsing of
859one block element does not affect the inline parsing of any other.
860
861## Container blocks and leaf blocks
862
863We can divide blocks into two types:
864[container blocks](#container-blocks),
865which can contain other blocks, and [leaf blocks](#leaf-blocks),
866which cannot.
867
868# Leaf blocks
869
870This section describes the different kinds of leaf block that make up a
871Markdown document.
872
873## Thematic breaks
874
875A line consisting of optionally up to three spaces of indentation, followed by a
876sequence of three or more matching `-`, `_`, or `*` characters, each followed
877optionally by any number of spaces or tabs, forms a
878[thematic break](@).
879
880```````````````````````````````` example
881***
882---
883___
884.
885<hr />
886<hr />
887<hr />
888````````````````````````````````
889
890
891Wrong characters:
892
893```````````````````````````````` example
894+++
895.
896<p>+++</p>
897````````````````````````````````
898
899
900```````````````````````````````` example
901===
902.
903<p>===</p>
904````````````````````````````````
905
906
907Not enough characters:
908
909```````````````````````````````` example
910--
911**
912__
913.
914<p>--
915**
916__</p>
917````````````````````````````````
918
919
920Up to three spaces of indentation are allowed:
921
922```````````````````````````````` example
923 ***
924 ***
925 ***
926.
927<hr />
928<hr />
929<hr />
930````````````````````````````````
931
932
933Four spaces of indentation is too many:
934
935```````````````````````````````` example
936 ***
937.
938<pre><code>***
939</code></pre>
940````````````````````````````````
941
942
943```````````````````````````````` example
944Foo
945 ***
946.
947<p>Foo
948***</p>
949````````````````````````````````
950
951
952More than three characters may be used:
953
954```````````````````````````````` example
955_____________________________________
956.
957<hr />
958````````````````````````````````
959
960
961Spaces and tabs are allowed between the characters:
962
963```````````````````````````````` example
964 - - -
965.
966<hr />
967````````````````````````````````
968
969
970```````````````````````````````` example
971 ** * ** * ** * **
972.
973<hr />
974````````````````````````````````
975
976
977```````````````````````````````` example
978- - - -
979.
980<hr />
981````````````````````````````````
982
983
984Spaces and tabs are allowed at the end:
985
986```````````````````````````````` example
987- - - -
988.
989<hr />
990````````````````````````````````
991
992
993However, no other characters may occur in the line:
994
995```````````````````````````````` example
996_ _ _ _ a
997
998a------
999
1000---a---
1001.
1002<p>_ _ _ _ a</p>
1003<p>a------</p>
1004<p>---a---</p>
1005````````````````````````````````
1006
1007
1008It is required that all of the characters other than spaces or tabs be the same.
1009So, this is not a thematic break:
1010
1011```````````````````````````````` example
1012 *-*
1013.
1014<p><em>-</em></p>
1015````````````````````````````````
1016
1017
1018Thematic breaks do not need blank lines before or after:
1019
1020```````````````````````````````` example
1021- foo
1022***
1023- bar
1024.
1025<ul>
1026<li>foo</li>
1027</ul>
1028<hr />
1029<ul>
1030<li>bar</li>
1031</ul>
1032````````````````````````````````
1033
1034
1035Thematic breaks can interrupt a paragraph:
1036
1037```````````````````````````````` example
1038Foo
1039***
1040bar
1041.
1042<p>Foo</p>
1043<hr />
1044<p>bar</p>
1045````````````````````````````````
1046
1047
1048If a line of dashes that meets the above conditions for being a
1049thematic break could also be interpreted as the underline of a [setext
1050heading], the interpretation as a
1051[setext heading] takes precedence. Thus, for example,
1052this is a setext heading, not a paragraph followed by a thematic break:
1053
1054```````````````````````````````` example
1055Foo
1056---
1057bar
1058.
1059<h2>Foo</h2>
1060<p>bar</p>
1061````````````````````````````````
1062
1063
1064When both a thematic break and a list item are possible
1065interpretations of a line, the thematic break takes precedence:
1066
1067```````````````````````````````` example
1068* Foo
1069* * *
1070* Bar
1071.
1072<ul>
1073<li>Foo</li>
1074</ul>
1075<hr />
1076<ul>
1077<li>Bar</li>
1078</ul>
1079````````````````````````````````
1080
1081
1082If you want a thematic break in a list item, use a different bullet:
1083
1084```````````````````````````````` example
1085- Foo
1086- * * *
1087.
1088<ul>
1089<li>Foo</li>
1090<li>
1091<hr />
1092</li>
1093</ul>
1094````````````````````````````````
1095
1096
1097## ATX headings
1098
1099An [ATX heading](@)
1100consists of a string of characters, parsed as inline content, between an
1101opening sequence of 1--6 unescaped `#` characters and an optional
1102closing sequence of any number of unescaped `#` characters.
1103The opening sequence of `#` characters must be followed by spaces or tabs, or
1104by the end of line. The optional closing sequence of `#`s must be preceded by
1105spaces or tabs and may be followed by spaces or tabs only. The opening
1106`#` character may be preceded by up to three spaces of indentation. The raw
1107contents of the heading are stripped of leading and trailing space or tabs
1108before being parsed as inline content. The heading level is equal to the number
1109of `#` characters in the opening sequence.
1110
1111Simple headings:
1112
1113```````````````````````````````` example
1114# foo
1115## foo
1116### foo
1117#### foo
1118##### foo
1119###### foo
1120.
1121<h1>foo</h1>
1122<h2>foo</h2>
1123<h3>foo</h3>
1124<h4>foo</h4>
1125<h5>foo</h5>
1126<h6>foo</h6>
1127````````````````````````````````
1128
1129
1130More than six `#` characters is not a heading:
1131
1132```````````````````````````````` example
1133####### foo
1134.
1135<p>####### foo</p>
1136````````````````````````````````
1137
1138
1139At least one space or tab is required between the `#` characters and the
1140heading's contents, unless the heading is empty. Note that many
1141implementations currently do not require the space. However, the
1142space was required by the
1143[original ATX implementation](http://www.aaronsw.com/2002/atx/atx.py),
1144and it helps prevent things like the following from being parsed as
1145headings:
1146
1147```````````````````````````````` example
1148#5 bolt
1149
1150#hashtag
1151.
1152<p>#5 bolt</p>
1153<p>#hashtag</p>
1154````````````````````````````````
1155
1156
1157This is not a heading, because the first `#` is escaped:
1158
1159```````````````````````````````` example
1160\## foo
1161.
1162<p>## foo</p>
1163````````````````````````````````
1164
1165
1166Contents are parsed as inlines:
1167
1168```````````````````````````````` example
1169# foo *bar* \*baz\*
1170.
1171<h1>foo <em>bar</em> *baz*</h1>
1172````````````````````````````````
1173
1174
1175Leading and trailing spaces or tabs are ignored in parsing inline content:
1176
1177```````````````````````````````` example
1178# foo
1179.
1180<h1>foo</h1>
1181````````````````````````````````
1182
1183
1184Up to three spaces of indentation are allowed:
1185
1186```````````````````````````````` example
1187 ### foo
1188 ## foo
1189 # foo
1190.
1191<h3>foo</h3>
1192<h2>foo</h2>
1193<h1>foo</h1>
1194````````````````````````````````
1195
1196
1197Four spaces of indentation is too many:
1198
1199```````````````````````````````` example
1200 # foo
1201.
1202<pre><code># foo
1203</code></pre>
1204````````````````````````````````
1205
1206
1207```````````````````````````````` example
1208foo
1209 # bar
1210.
1211<p>foo
1212# bar</p>
1213````````````````````````````````
1214
1215
1216A closing sequence of `#` characters is optional:
1217
1218```````````````````````````````` example
1219## foo ##
1220 ### bar ###
1221.
1222<h2>foo</h2>
1223<h3>bar</h3>
1224````````````````````````````````
1225
1226
1227It need not be the same length as the opening sequence:
1228
1229```````````````````````````````` example
1230# foo ##################################
1231##### foo ##
1232.
1233<h1>foo</h1>
1234<h5>foo</h5>
1235````````````````````````````````
1236
1237
1238Spaces or tabs are allowed after the closing sequence:
1239
1240```````````````````````````````` example
1241### foo ###
1242.
1243<h3>foo</h3>
1244````````````````````````````````
1245
1246
1247A sequence of `#` characters with anything but spaces or tabs following it
1248is not a closing sequence, but counts as part of the contents of the
1249heading:
1250
1251```````````````````````````````` example
1252### foo ### b
1253.
1254<h3>foo ### b</h3>
1255````````````````````````````````
1256
1257
1258The closing sequence must be preceded by a space or tab:
1259
1260```````````````````````````````` example
1261# foo#
1262.
1263<h1>foo#</h1>
1264````````````````````````````````
1265
1266
1267Backslash-escaped `#` characters do not count as part
1268of the closing sequence:
1269
1270```````````````````````````````` example
1271### foo \###
1272## foo #\##
1273# foo \#
1274.
1275<h3>foo ###</h3>
1276<h2>foo ###</h2>
1277<h1>foo #</h1>
1278````````````````````````````````
1279
1280
1281ATX headings need not be separated from surrounding content by blank
1282lines, and they can interrupt paragraphs:
1283
1284```````````````````````````````` example
1285****
1286## foo
1287****
1288.
1289<hr />
1290<h2>foo</h2>
1291<hr />
1292````````````````````````````````
1293
1294
1295```````````````````````````````` example
1296Foo bar
1297# baz
1298Bar foo
1299.
1300<p>Foo bar</p>
1301<h1>baz</h1>
1302<p>Bar foo</p>
1303````````````````````````````````
1304
1305
1306ATX headings can be empty:
1307
1308```````````````````````````````` example
1309##
1310#
1311### ###
1312.
1313<h2></h2>
1314<h1></h1>
1315<h3></h3>
1316````````````````````````````````
1317
1318
1319## Setext headings
1320
1321A [setext heading](@) consists of one or more
1322lines of text, not interrupted by a blank line, of which the first line does not
1323have more than 3 spaces of indentation, followed by
1324a [setext heading underline]. The lines of text must be such
1325that, were they not followed by the setext heading underline,
1326they would be interpreted as a paragraph: they cannot be
1327interpretable as a [code fence], [ATX heading][ATX headings],
1328[block quote][block quotes], [thematic break][thematic breaks],
1329[list item][list items], or [HTML block][HTML blocks].
1330
1331A [setext heading underline](@) is a sequence of
1332`=` characters or a sequence of `-` characters, with no more than 3
1333spaces of indentation and any number of trailing spaces or tabs. If a line
1334containing a single `-` can be interpreted as an
1335empty [list items], it should be interpreted this way
1336and not as a [setext heading underline].
1337
1338The heading is a level 1 heading if `=` characters are used in
1339the [setext heading underline], and a level 2 heading if `-`
1340characters are used. The contents of the heading are the result
1341of parsing the preceding lines of text as CommonMark inline
1342content.
1343
1344In general, a setext heading need not be preceded or followed by a
1345blank line. However, it cannot interrupt a paragraph, so when a
1346setext heading comes after a paragraph, a blank line is needed between
1347them.
1348
1349Simple examples:
1350
1351```````````````````````````````` example
1352Foo *bar*
1353=========
1354
1355Foo *bar*
1356---------
1357.
1358<h1>Foo <em>bar</em></h1>
1359<h2>Foo <em>bar</em></h2>
1360````````````````````````````````
1361
1362
1363The content of the header may span more than one line:
1364
1365```````````````````````````````` example
1366Foo *bar
1367baz*
1368====
1369.
1370<h1>Foo <em>bar
1371baz</em></h1>
1372````````````````````````````````
1373
1374The contents are the result of parsing the headings's raw
1375content as inlines. The heading's raw content is formed by
1376concatenating the lines and removing initial and final
1377spaces or tabs.
1378
1379```````````````````````````````` example
1380 Foo *bar
1381baz*→
1382====
1383.
1384<h1>Foo <em>bar
1385baz</em></h1>
1386````````````````````````````````
1387
1388
1389The underlining can be any length:
1390
1391```````````````````````````````` example
1392Foo
1393-------------------------
1394
1395Foo
1396=
1397.
1398<h2>Foo</h2>
1399<h1>Foo</h1>
1400````````````````````````````````
1401
1402
1403The heading content can be preceded by up to three spaces of indentation, and
1404need not line up with the underlining:
1405
1406```````````````````````````````` example
1407 Foo
1408---
1409
1410 Foo
1411-----
1412
1413 Foo
1414 ===
1415.
1416<h2>Foo</h2>
1417<h2>Foo</h2>
1418<h1>Foo</h1>
1419````````````````````````````````
1420
1421
1422Four spaces of indentation is too many:
1423
1424```````````````````````````````` example
1425 Foo
1426 ---
1427
1428 Foo
1429---
1430.
1431<pre><code>Foo
1432---
1433
1434Foo
1435</code></pre>
1436<hr />
1437````````````````````````````````
1438
1439
1440The setext heading underline can be preceded by up to three spaces of
1441indentation, and may have trailing spaces or tabs:
1442
1443```````````````````````````````` example
1444Foo
1445 ----
1446.
1447<h2>Foo</h2>
1448````````````````````````````````
1449
1450
1451Four spaces of indentation is too many:
1452
1453```````````````````````````````` example
1454Foo
1455 ---
1456.
1457<p>Foo
1458---</p>
1459````````````````````````````````
1460
1461
1462The setext heading underline cannot contain internal spaces or tabs:
1463
1464```````````````````````````````` example
1465Foo
1466= =
1467
1468Foo
1469--- -
1470.
1471<p>Foo
1472= =</p>
1473<p>Foo</p>
1474<hr />
1475````````````````````````````````
1476
1477
1478Trailing spaces or tabs in the content line do not cause a hard line break:
1479
1480```````````````````````````````` example
1481Foo
1482-----
1483.
1484<h2>Foo</h2>
1485````````````````````````````````
1486
1487
1488Nor does a backslash at the end:
1489
1490```````````````````````````````` example
1491Foo\
1492----
1493.
1494<h2>Foo\</h2>
1495````````````````````````````````
1496
1497
1498Since indicators of block structure take precedence over
1499indicators of inline structure, the following are setext headings:
1500
1501```````````````````````````````` example
1502`Foo
1503----
1504`
1505
1506<a title="a lot
1507---
1508of dashes"/>
1509.
1510<h2>`Foo</h2>
1511<p>`</p>
1512<h2>&lt;a title=&quot;a lot</h2>
1513<p>of dashes&quot;/&gt;</p>
1514````````````````````````````````
1515
1516
1517The setext heading underline cannot be a [lazy continuation
1518line] in a list item or block quote:
1519
1520```````````````````````````````` example
1521> Foo
1522---
1523.
1524<blockquote>
1525<p>Foo</p>
1526</blockquote>
1527<hr />
1528````````````````````````````````
1529
1530
1531```````````````````````````````` example
1532> foo
1533bar
1534===
1535.
1536<blockquote>
1537<p>foo
1538bar
1539===</p>
1540</blockquote>
1541````````````````````````````````
1542
1543
1544```````````````````````````````` example
1545- Foo
1546---
1547.
1548<ul>
1549<li>Foo</li>
1550</ul>
1551<hr />
1552````````````````````````````````
1553
1554
1555A blank line is needed between a paragraph and a following
1556setext heading, since otherwise the paragraph becomes part
1557of the heading's content:
1558
1559```````````````````````````````` example
1560Foo
1561Bar
1562---
1563.
1564<h2>Foo
1565Bar</h2>
1566````````````````````````````````
1567
1568
1569But in general a blank line is not required before or after
1570setext headings:
1571
1572```````````````````````````````` example
1573---
1574Foo
1575---
1576Bar
1577---
1578Baz
1579.
1580<hr />
1581<h2>Foo</h2>
1582<h2>Bar</h2>
1583<p>Baz</p>
1584````````````````````````````````
1585
1586
1587Setext headings cannot be empty:
1588
1589```````````````````````````````` example
1590
1591====
1592.
1593<p>====</p>
1594````````````````````````````````
1595
1596
1597Setext heading text lines must not be interpretable as block
1598constructs other than paragraphs. So, the line of dashes
1599in these examples gets interpreted as a thematic break:
1600
1601```````````````````````````````` example
1602---
1603---
1604.
1605<hr />
1606<hr />
1607````````````````````````````````
1608
1609
1610```````````````````````````````` example
1611- foo
1612-----
1613.
1614<ul>
1615<li>foo</li>
1616</ul>
1617<hr />
1618````````````````````````````````
1619
1620
1621```````````````````````````````` example
1622 foo
1623---
1624.
1625<pre><code>foo
1626</code></pre>
1627<hr />
1628````````````````````````````````
1629
1630
1631```````````````````````````````` example
1632> foo
1633-----
1634.
1635<blockquote>
1636<p>foo</p>
1637</blockquote>
1638<hr />
1639````````````````````````````````
1640
1641
1642If you want a heading with `> foo` as its literal text, you can
1643use backslash escapes:
1644
1645```````````````````````````````` example
1646\> foo
1647------
1648.
1649<h2>&gt; foo</h2>
1650````````````````````````````````
1651
1652
1653**Compatibility note:** Most existing Markdown implementations
1654do not allow the text of setext headings to span multiple lines.
1655But there is no consensus about how to interpret
1656
1657``` markdown
1658Foo
1659bar
1660---
1661baz
1662```
1663
1664One can find four different interpretations:
1665
16661. paragraph "Foo", heading "bar", paragraph "baz"
16672. paragraph "Foo bar", thematic break, paragraph "baz"
16683. paragraph "Foo bar --- baz"
16694. heading "Foo bar", paragraph "baz"
1670
1671We find interpretation 4 most natural, and interpretation 4
1672increases the expressive power of CommonMark, by allowing
1673multiline headings. Authors who want interpretation 1 can
1674put a blank line after the first paragraph:
1675
1676```````````````````````````````` example
1677Foo
1678
1679bar
1680---
1681baz
1682.
1683<p>Foo</p>
1684<h2>bar</h2>
1685<p>baz</p>
1686````````````````````````````````
1687
1688
1689Authors who want interpretation 2 can put blank lines around
1690the thematic break,
1691
1692```````````````````````````````` example
1693Foo
1694bar
1695
1696---
1697
1698baz
1699.
1700<p>Foo
1701bar</p>
1702<hr />
1703<p>baz</p>
1704````````````````````````````````
1705
1706
1707or use a thematic break that cannot count as a [setext heading
1708underline], such as
1709
1710```````````````````````````````` example
1711Foo
1712bar
1713* * *
1714baz
1715.
1716<p>Foo
1717bar</p>
1718<hr />
1719<p>baz</p>
1720````````````````````````````````
1721
1722
1723Authors who want interpretation 3 can use backslash escapes:
1724
1725```````````````````````````````` example
1726Foo
1727bar
1728\---
1729baz
1730.
1731<p>Foo
1732bar
1733---
1734baz</p>
1735````````````````````````````````
1736
1737
1738## Indented code blocks
1739
1740An [indented code block](@) is composed of one or more
1741[indented chunks] separated by blank lines.
1742An [indented chunk](@) is a sequence of non-blank lines,
1743each preceded by four or more spaces of indentation. The contents of the code
1744block are the literal contents of the lines, including trailing
1745[line endings], minus four spaces of indentation.
1746An indented code block has no [info string].
1747
1748An indented code block cannot interrupt a paragraph, so there must be
1749a blank line between a paragraph and a following indented code block.
1750(A blank line is not needed, however, between a code block and a following
1751paragraph.)
1752
1753```````````````````````````````` example
1754 a simple
1755 indented code block
1756.
1757<pre><code>a simple
1758 indented code block
1759</code></pre>
1760````````````````````````````````
1761
1762
1763If there is any ambiguity between an interpretation of indentation
1764as a code block and as indicating that material belongs to a [list
1765item][list items], the list item interpretation takes precedence:
1766
1767```````````````````````````````` example
1768 - foo
1769
1770 bar
1771.
1772<ul>
1773<li>
1774<p>foo</p>
1775<p>bar</p>
1776</li>
1777</ul>
1778````````````````````````````````
1779
1780
1781```````````````````````````````` example
17821. foo
1783
1784 - bar
1785.
1786<ol>
1787<li>
1788<p>foo</p>
1789<ul>
1790<li>bar</li>
1791</ul>
1792</li>
1793</ol>
1794````````````````````````````````
1795
1796
1797
1798The contents of a code block are literal text, and do not get parsed
1799as Markdown:
1800
1801```````````````````````````````` example
1802 <a/>
1803 *hi*
1804
1805 - one
1806.
1807<pre><code>&lt;a/&gt;
1808*hi*
1809
1810- one
1811</code></pre>
1812````````````````````````````````
1813
1814
1815Here we have three chunks separated by blank lines:
1816
1817```````````````````````````````` example
1818 chunk1
1819
1820 chunk2
1821
1822
1823
1824 chunk3
1825.
1826<pre><code>chunk1
1827
1828chunk2
1829
1830
1831
1832chunk3
1833</code></pre>
1834````````````````````````````````
1835
1836
1837Any initial spaces or tabs beyond four spaces of indentation will be included in
1838the content, even in interior blank lines:
1839
1840```````````````````````````````` example
1841 chunk1
1842
1843 chunk2
1844.
1845<pre><code>chunk1
1846
1847 chunk2
1848</code></pre>
1849````````````````````````````````
1850
1851
1852An indented code block cannot interrupt a paragraph. (This
1853allows hanging indents and the like.)
1854
1855```````````````````````````````` example
1856Foo
1857 bar
1858
1859.
1860<p>Foo
1861bar</p>
1862````````````````````````````````
1863
1864
1865However, any non-blank line with fewer than four spaces of indentation ends
1866the code block immediately. So a paragraph may occur immediately
1867after indented code:
1868
1869```````````````````````````````` example
1870 foo
1871bar
1872.
1873<pre><code>foo
1874</code></pre>
1875<p>bar</p>
1876````````````````````````````````
1877
1878
1879And indented code can occur immediately before and after other kinds of
1880blocks:
1881
1882```````````````````````````````` example
1883# Heading
1884 foo
1885Heading
1886------
1887 foo
1888----
1889.
1890<h1>Heading</h1>
1891<pre><code>foo
1892</code></pre>
1893<h2>Heading</h2>
1894<pre><code>foo
1895</code></pre>
1896<hr />
1897````````````````````````````````
1898
1899
1900The first line can be preceded by more than four spaces of indentation:
1901
1902```````````````````````````````` example
1903 foo
1904 bar
1905.
1906<pre><code> foo
1907bar
1908</code></pre>
1909````````````````````````````````
1910
1911
1912Blank lines preceding or following an indented code block
1913are not included in it:
1914
1915```````````````````````````````` example
1916
1917
1918 foo
1919
1920
1921.
1922<pre><code>foo
1923</code></pre>
1924````````````````````````````````
1925
1926
1927Trailing spaces or tabs are included in the code block's content:
1928
1929```````````````````````````````` example
1930 foo
1931.
1932<pre><code>foo
1933</code></pre>
1934````````````````````````````````
1935
1936
1937
1938## Fenced code blocks
1939
1940A [code fence](@) is a sequence
1941of at least three consecutive backtick characters (`` ` ``) or
1942tildes (`~`). (Tildes and backticks cannot be mixed.)
1943A [fenced code block](@)
1944begins with a code fence, preceded by up to three spaces of indentation.
1945
1946The line with the opening code fence may optionally contain some text
1947following the code fence; this is trimmed of leading and trailing
1948spaces or tabs and called the [info string](@). If the [info string] comes
1949after a backtick fence, it may not contain any backtick
1950characters. (The reason for this restriction is that otherwise
1951some inline code would be incorrectly interpreted as the
1952beginning of a fenced code block.)
1953
1954The content of the code block consists of all subsequent lines, until
1955a closing [code fence] of the same type as the code block
1956began with (backticks or tildes), and with at least as many backticks
1957or tildes as the opening code fence. If the leading code fence is
1958preceded by N spaces of indentation, then up to N spaces of indentation are
1959removed from each line of the content (if present). (If a content line is not
1960indented, it is preserved unchanged. If it is indented N spaces or less, all
1961of the indentation is removed.)
1962
1963The closing code fence may be preceded by up to three spaces of indentation, and
1964may be followed only by spaces or tabs, which are ignored. If the end of the
1965containing block (or document) is reached and no closing code fence
1966has been found, the code block contains all of the lines after the
1967opening code fence until the end of the containing block (or
1968document). (An alternative spec would require backtracking in the
1969event that a closing code fence is not found. But this makes parsing
1970much less efficient, and there seems to be no real down side to the
1971behavior described here.)
1972
1973A fenced code block may interrupt a paragraph, and does not require
1974a blank line either before or after.
1975
1976The content of a code fence is treated as literal text, not parsed
1977as inlines. The first word of the [info string] is typically used to
1978specify the language of the code sample, and rendered in the `class`
1979attribute of the `code` tag. However, this spec does not mandate any
1980particular treatment of the [info string].
1981
1982Here is a simple example with backticks:
1983
1984```````````````````````````````` example
1985```
1986<
1987 >
1988```
1989.
1990<pre><code>&lt;
1991 &gt;
1992</code></pre>
1993````````````````````````````````
1994
1995
1996With tildes:
1997
1998```````````````````````````````` example
1999~~~
2000<
2001 >
2002~~~
2003.
2004<pre><code>&lt;
2005 &gt;
2006</code></pre>
2007````````````````````````````````
2008
2009Fewer than three backticks is not enough:
2010
2011```````````````````````````````` example
2012``
2013foo
2014``
2015.
2016<p><code>foo</code></p>
2017````````````````````````````````
2018
2019The closing code fence must use the same character as the opening
2020fence:
2021
2022```````````````````````````````` example
2023```
2024aaa
2025~~~
2026```
2027.
2028<pre><code>aaa
2029~~~
2030</code></pre>
2031````````````````````````````````
2032
2033
2034```````````````````````````````` example
2035~~~
2036aaa
2037```
2038~~~
2039.
2040<pre><code>aaa
2041```
2042</code></pre>
2043````````````````````````````````
2044
2045
2046The closing code fence must be at least as long as the opening fence:
2047
2048```````````````````````````````` example
2049````
2050aaa
2051```
2052``````
2053.
2054<pre><code>aaa
2055```
2056</code></pre>
2057````````````````````````````````
2058
2059
2060```````````````````````````````` example
2061~~~~
2062aaa
2063~~~
2064~~~~
2065.
2066<pre><code>aaa
2067~~~
2068</code></pre>
2069````````````````````````````````
2070
2071
2072Unclosed code blocks are closed by the end of the document
2073(or the enclosing [block quote][block quotes] or [list item][list items]):
2074
2075```````````````````````````````` example
2076```
2077.
2078<pre><code></code></pre>
2079````````````````````````````````
2080
2081
2082```````````````````````````````` example
2083`````
2084
2085```
2086aaa
2087.
2088<pre><code>
2089```
2090aaa
2091</code></pre>
2092````````````````````````````````
2093
2094
2095```````````````````````````````` example
2096> ```
2097> aaa
2098
2099bbb
2100.
2101<blockquote>
2102<pre><code>aaa
2103</code></pre>
2104</blockquote>
2105<p>bbb</p>
2106````````````````````````````````
2107
2108
2109A code block can have all empty lines as its content:
2110
2111```````````````````````````````` example
2112```
2113
2114
2115```
2116.
2117<pre><code>
2118
2119</code></pre>
2120````````````````````````````````
2121
2122
2123A code block can be empty:
2124
2125```````````````````````````````` example
2126```
2127```
2128.
2129<pre><code></code></pre>
2130````````````````````````````````
2131
2132
2133Fences can be indented. If the opening fence is indented,
2134content lines will have equivalent opening indentation removed,
2135if present:
2136
2137```````````````````````````````` example
2138 ```
2139 aaa
2140aaa
2141```
2142.
2143<pre><code>aaa
2144aaa
2145</code></pre>
2146````````````````````````````````
2147
2148
2149```````````````````````````````` example
2150 ```
2151aaa
2152 aaa
2153aaa
2154 ```
2155.
2156<pre><code>aaa
2157aaa
2158aaa
2159</code></pre>
2160````````````````````````````````
2161
2162
2163```````````````````````````````` example
2164 ```
2165 aaa
2166 aaa
2167 aaa
2168 ```
2169.
2170<pre><code>aaa
2171 aaa
2172aaa
2173</code></pre>
2174````````````````````````````````
2175
2176
2177Four spaces of indentation is too many:
2178
2179```````````````````````````````` example
2180 ```
2181 aaa
2182 ```
2183.
2184<pre><code>```
2185aaa
2186```
2187</code></pre>
2188````````````````````````````````
2189
2190
2191Closing fences may be preceded by up to three spaces of indentation, and their
2192indentation need not match that of the opening fence:
2193
2194```````````````````````````````` example
2195```
2196aaa
2197 ```
2198.
2199<pre><code>aaa
2200</code></pre>
2201````````````````````````````````
2202
2203
2204```````````````````````````````` example
2205 ```
2206aaa
2207 ```
2208.
2209<pre><code>aaa
2210</code></pre>
2211````````````````````````````````
2212
2213
2214This is not a closing fence, because it is indented 4 spaces:
2215
2216```````````````````````````````` example
2217```
2218aaa
2219 ```
2220.
2221<pre><code>aaa
2222 ```
2223</code></pre>
2224````````````````````````````````
2225
2226
2227
2228Code fences (opening and closing) cannot contain internal spaces or tabs:
2229
2230```````````````````````````````` example
2231``` ```
2232aaa
2233.
2234<p><code> </code>
2235aaa</p>
2236````````````````````````````````
2237
2238
2239```````````````````````````````` example
2240~~~~~~
2241aaa
2242~~~ ~~
2243.
2244<pre><code>aaa
2245~~~ ~~
2246</code></pre>
2247````````````````````````````````
2248
2249
2250Fenced code blocks can interrupt paragraphs, and can be followed
2251directly by paragraphs, without a blank line between:
2252
2253```````````````````````````````` example
2254foo
2255```
2256bar
2257```
2258baz
2259.
2260<p>foo</p>
2261<pre><code>bar
2262</code></pre>
2263<p>baz</p>
2264````````````````````````````````
2265
2266
2267Other blocks can also occur before and after fenced code blocks
2268without an intervening blank line:
2269
2270```````````````````````````````` example
2271foo
2272---
2273~~~
2274bar
2275~~~
2276# baz
2277.
2278<h2>foo</h2>
2279<pre><code>bar
2280</code></pre>
2281<h1>baz</h1>
2282````````````````````````````````
2283
2284
2285An [info string] can be provided after the opening code fence.
2286Although this spec doesn't mandate any particular treatment of
2287the info string, the first word is typically used to specify
2288the language of the code block. In HTML output, the language is
2289normally indicated by adding a class to the `code` element consisting
2290of `language-` followed by the language name.
2291
2292```````````````````````````````` example
2293```ruby
2294def foo(x)
2295 return 3
2296end
2297```
2298.
2299<pre><code class="language-ruby">def foo(x)
2300 return 3
2301end
2302</code></pre>
2303````````````````````````````````
2304
2305
2306```````````````````````````````` example
2307~~~~ ruby startline=3 $%@#$
2308def foo(x)
2309 return 3
2310end
2311~~~~~~~
2312.
2313<pre><code class="language-ruby">def foo(x)
2314 return 3
2315end
2316</code></pre>
2317````````````````````````````````
2318
2319
2320```````````````````````````````` example
2321````;
2322````
2323.
2324<pre><code class="language-;"></code></pre>
2325````````````````````````````````
2326
2327
2328[Info strings] for backtick code blocks cannot contain backticks:
2329
2330```````````````````````````````` example
2331``` aa ```
2332foo
2333.
2334<p><code>aa</code>
2335foo</p>
2336````````````````````````````````
2337
2338
2339[Info strings] for tilde code blocks can contain backticks and tildes:
2340
2341```````````````````````````````` example
2342~~~ aa ``` ~~~
2343foo
2344~~~
2345.
2346<pre><code class="language-aa">foo
2347</code></pre>
2348````````````````````````````````
2349
2350
2351Closing code fences cannot have [info strings]:
2352
2353```````````````````````````````` example
2354```
2355``` aaa
2356```
2357.
2358<pre><code>``` aaa
2359</code></pre>
2360````````````````````````````````
2361
2362
2363
2364## HTML blocks
2365
2366An [HTML block](@) is a group of lines that is treated
2367as raw HTML (and will not be escaped in HTML output).
2368
2369There are seven kinds of [HTML block], which can be defined by their
2370start and end conditions. The block begins with a line that meets a
2371[start condition](@) (after up to three optional spaces of indentation).
2372It ends with the first subsequent line that meets a matching
2373[end condition](@), or the last line of the document, or the last line of
2374the [container block](#container-blocks) containing the current HTML
2375block, if no line is encountered that meets the [end condition]. If
2376the first line meets both the [start condition] and the [end
2377condition], the block will contain just that line.
2378
23791. **Start condition:** line begins with the string `<pre`,
2380`<script`, `<style`, or `<textarea` (case-insensitive), followed by a space,
2381a tab, the string `>`, or the end of the line.\
2382**End condition:** line contains an end tag
2383`</pre>`, `</script>`, `</style>`, or `</textarea>` (case-insensitive; it
2384need not match the start tag).
2385
23862. **Start condition:** line begins with the string `<!--`.\
2387**End condition:** line contains the string `-->`.
2388
23893. **Start condition:** line begins with the string `<?`.\
2390**End condition:** line contains the string `?>`.
2391
23924. **Start condition:** line begins with the string `<!`
2393followed by an ASCII letter.\
2394**End condition:** line contains the character `>`.
2395
23965. **Start condition:** line begins with the string
2397`<![CDATA[`.\
2398**End condition:** line contains the string `]]>`.
2399
24006. **Start condition:** line begins the string `<` or `</`
2401followed by one of the strings (case-insensitive) `address`,
2402`article`, `aside`, `base`, `basefont`, `blockquote`, `body`,
2403`caption`, `center`, `col`, `colgroup`, `dd`, `details`, `dialog`,
2404`dir`, `div`, `dl`, `dt`, `fieldset`, `figcaption`, `figure`,
2405`footer`, `form`, `frame`, `frameset`,
2406`h1`, `h2`, `h3`, `h4`, `h5`, `h6`, `head`, `header`, `hr`,
2407`html`, `iframe`, `legend`, `li`, `link`, `main`, `menu`, `menuitem`,
2408`nav`, `noframes`, `ol`, `optgroup`, `option`, `p`, `param`,
2409`section`, `source`, `summary`, `table`, `tbody`, `td`,
2410`tfoot`, `th`, `thead`, `title`, `tr`, `track`, `ul`, followed
2411by a space, a tab, the end of the line, the string `>`, or
2412the string `/>`.\
2413**End condition:** line is followed by a [blank line].
2414
24157. **Start condition:** line begins with a complete [open tag]
2416(with any [tag name] other than `pre`, `script`,
2417`style`, or `textarea`) or a complete [closing tag],
2418followed by zero or more spaces and tabs, followed by the end of the line.\
2419**End condition:** line is followed by a [blank line].
2420
2421HTML blocks continue until they are closed by their appropriate
2422[end condition], or the last line of the document or other [container
2423block](#container-blocks). This means any HTML **within an HTML
2424block** that might otherwise be recognised as a start condition will
2425be ignored by the parser and passed through as-is, without changing
2426the parser's state.
2427
2428For instance, `<pre>` within an HTML block started by `<table>` will not affect
2429the parser state; as the HTML block was started in by start condition 6, it
2430will end at any blank line. This can be surprising:
2431
2432```````````````````````````````` example
2433<table><tr><td>
2434<pre>
2435**Hello**,
2436
2437_world_.
2438</pre>
2439</td></tr></table>
2440.
2441<table><tr><td>
2442<pre>
2443**Hello**,
2444<p><em>world</em>.
2445</pre></p>
2446</td></tr></table>
2447````````````````````````````````
2448
2449In this case, the HTML block is terminated by the blank line — the `**Hello**`
2450text remains verbatim — and regular parsing resumes, with a paragraph,
2451emphasised `world` and inline and block HTML following.
2452
2453All types of [HTML blocks] except type 7 may interrupt
2454a paragraph. Blocks of type 7 may not interrupt a paragraph.
2455(This restriction is intended to prevent unwanted interpretation
2456of long tags inside a wrapped paragraph as starting HTML blocks.)
2457
2458Some simple examples follow. Here are some basic HTML blocks
2459of type 6:
2460
2461```````````````````````````````` example
2462<table>
2463 <tr>
2464 <td>
2465 hi
2466 </td>
2467 </tr>
2468</table>
2469
2470okay.
2471.
2472<table>
2473 <tr>
2474 <td>
2475 hi
2476 </td>
2477 </tr>
2478</table>
2479<p>okay.</p>
2480````````````````````````````````
2481
2482
2483```````````````````````````````` example
2484 <div>
2485 *hello*
2486 <foo><a>
2487.
2488 <div>
2489 *hello*
2490 <foo><a>
2491````````````````````````````````
2492
2493
2494A block can also start with a closing tag:
2495
2496```````````````````````````````` example
2497</div>
2498*foo*
2499.
2500</div>
2501*foo*
2502````````````````````````````````
2503
2504
2505Here we have two HTML blocks with a Markdown paragraph between them:
2506
2507```````````````````````````````` example
2508<DIV CLASS="foo">
2509
2510*Markdown*
2511
2512</DIV>
2513.
2514<DIV CLASS="foo">
2515<p><em>Markdown</em></p>
2516</DIV>
2517````````````````````````````````
2518
2519
2520The tag on the first line can be partial, as long
2521as it is split where there would be whitespace:
2522
2523```````````````````````````````` example
2524<div id="foo"
2525 class="bar">
2526</div>
2527.
2528<div id="foo"
2529 class="bar">
2530</div>
2531````````````````````````````````
2532
2533
2534```````````````````````````````` example
2535<div id="foo" class="bar
2536 baz">
2537</div>
2538.
2539<div id="foo" class="bar
2540 baz">
2541</div>
2542````````````````````````````````
2543
2544
2545An open tag need not be closed:
2546```````````````````````````````` example
2547<div>
2548*foo*
2549
2550*bar*
2551.
2552<div>
2553*foo*
2554<p><em>bar</em></p>
2555````````````````````````````````
2556
2557
2558
2559A partial tag need not even be completed (garbage
2560in, garbage out):
2561
2562```````````````````````````````` example
2563<div id="foo"
2564*hi*
2565.
2566<div id="foo"
2567*hi*
2568````````````````````````````````
2569
2570
2571```````````````````````````````` example
2572<div class
2573foo
2574.
2575<div class
2576foo
2577````````````````````````````````
2578
2579
2580The initial tag doesn't even need to be a valid
2581tag, as long as it starts like one:
2582
2583```````````````````````````````` example
2584<div *???-&&&-<---
2585*foo*
2586.
2587<div *???-&&&-<---
2588*foo*
2589````````````````````````````````
2590
2591
2592In type 6 blocks, the initial tag need not be on a line by
2593itself:
2594
2595```````````````````````````````` example
2596<div><a href="bar">*foo*</a></div>
2597.
2598<div><a href="bar">*foo*</a></div>
2599````````````````````````````````
2600
2601
2602```````````````````````````````` example
2603<table><tr><td>
2604foo
2605</td></tr></table>
2606.
2607<table><tr><td>
2608foo
2609</td></tr></table>
2610````````````````````````````````
2611
2612
2613Everything until the next blank line or end of document
2614gets included in the HTML block. So, in the following
2615example, what looks like a Markdown code block
2616is actually part of the HTML block, which continues until a blank
2617line or the end of the document is reached:
2618
2619```````````````````````````````` example
2620<div></div>
2621``` c
2622int x = 33;
2623```
2624.
2625<div></div>
2626``` c
2627int x = 33;
2628```
2629````````````````````````````````
2630
2631
2632To start an [HTML block] with a tag that is *not* in the
2633list of block-level tags in (6), you must put the tag by
2634itself on the first line (and it must be complete):
2635
2636```````````````````````````````` example
2637<a href="foo">
2638*bar*
2639</a>
2640.
2641<a href="foo">
2642*bar*
2643</a>
2644````````````````````````````````
2645
2646
2647In type 7 blocks, the [tag name] can be anything:
2648
2649```````````````````````````````` example
2650<Warning>
2651*bar*
2652</Warning>
2653.
2654<Warning>
2655*bar*
2656</Warning>
2657````````````````````````````````
2658
2659
2660```````````````````````````````` example
2661<i class="foo">
2662*bar*
2663</i>
2664.
2665<i class="foo">
2666*bar*
2667</i>
2668````````````````````````````````
2669
2670
2671```````````````````````````````` example
2672</ins>
2673*bar*
2674.
2675</ins>
2676*bar*
2677````````````````````````````````
2678
2679
2680These rules are designed to allow us to work with tags that
2681can function as either block-level or inline-level tags.
2682The `<del>` tag is a nice example. We can surround content with
2683`<del>` tags in three different ways. In this case, we get a raw
2684HTML block, because the `<del>` tag is on a line by itself:
2685
2686```````````````````````````````` example
2687<del>
2688*foo*
2689</del>
2690.
2691<del>
2692*foo*
2693</del>
2694````````````````````````````````
2695
2696
2697In this case, we get a raw HTML block that just includes
2698the `<del>` tag (because it ends with the following blank
2699line). So the contents get interpreted as CommonMark:
2700
2701```````````````````````````````` example
2702<del>
2703
2704*foo*
2705
2706</del>
2707.
2708<del>
2709<p><em>foo</em></p>
2710</del>
2711````````````````````````````````
2712
2713
2714Finally, in this case, the `<del>` tags are interpreted
2715as [raw HTML] *inside* the CommonMark paragraph. (Because
2716the tag is not on a line by itself, we get inline HTML
2717rather than an [HTML block].)
2718
2719```````````````````````````````` example
2720<del>*foo*</del>
2721.
2722<p><del><em>foo</em></del></p>
2723````````````````````````````````
2724
2725
2726HTML tags designed to contain literal content
2727(`pre`, `script`, `style`, `textarea`), comments, processing instructions,
2728and declarations are treated somewhat differently.
2729Instead of ending at the first blank line, these blocks
2730end at the first line containing a corresponding end tag.
2731As a result, these blocks can contain blank lines:
2732
2733A pre tag (type 1):
2734
2735```````````````````````````````` example
2736<pre language="haskell"><code>
2737import Text.HTML.TagSoup
2738
2739main :: IO ()
2740main = print $ parseTags tags
2741</code></pre>
2742okay
2743.
2744<pre language="haskell"><code>
2745import Text.HTML.TagSoup
2746
2747main :: IO ()
2748main = print $ parseTags tags
2749</code></pre>
2750<p>okay</p>
2751````````````````````````````````
2752
2753
2754A script tag (type 1):
2755
2756```````````````````````````````` example
2757<script type="text/javascript">
2758// JavaScript example
2759
2760document.getElementById("demo").innerHTML = "Hello JavaScript!";
2761</script>
2762okay
2763.
2764<script type="text/javascript">
2765// JavaScript example
2766
2767document.getElementById("demo").innerHTML = "Hello JavaScript!";
2768</script>
2769<p>okay</p>
2770````````````````````````````````
2771
2772
2773A textarea tag (type 1):
2774
2775```````````````````````````````` example
2776<textarea>
2777
2778*foo*
2779
2780_bar_
2781
2782</textarea>
2783.
2784<textarea>
2785
2786*foo*
2787
2788_bar_
2789
2790</textarea>
2791````````````````````````````````
2792
2793A style tag (type 1):
2794
2795```````````````````````````````` example
2796<style
2797 type="text/css">
2798h1 {color:red;}
2799
2800p {color:blue;}
2801</style>
2802okay
2803.
2804<style
2805 type="text/css">
2806h1 {color:red;}
2807
2808p {color:blue;}
2809</style>
2810<p>okay</p>
2811````````````````````````````````
2812
2813
2814If there is no matching end tag, the block will end at the
2815end of the document (or the enclosing [block quote][block quotes]
2816or [list item][list items]):
2817
2818```````````````````````````````` example
2819<style
2820 type="text/css">
2821
2822foo
2823.
2824<style
2825 type="text/css">
2826
2827foo
2828````````````````````````````````
2829
2830
2831```````````````````````````````` example
2832> <div>
2833> foo
2834
2835bar
2836.
2837<blockquote>
2838<div>
2839foo
2840</blockquote>
2841<p>bar</p>
2842````````````````````````````````
2843
2844
2845```````````````````````````````` example
2846- <div>
2847- foo
2848.
2849<ul>
2850<li>
2851<div>
2852</li>
2853<li>foo</li>
2854</ul>
2855````````````````````````````````
2856
2857
2858The end tag can occur on the same line as the start tag:
2859
2860```````````````````````````````` example
2861<style>p{color:red;}</style>
2862*foo*
2863.
2864<style>p{color:red;}</style>
2865<p><em>foo</em></p>
2866````````````````````````````````
2867
2868
2869```````````````````````````````` example
2870<!-- foo -->*bar*
2871*baz*
2872.
2873<!-- foo -->*bar*
2874<p><em>baz</em></p>
2875````````````````````````````````
2876
2877
2878Note that anything on the last line after the
2879end tag will be included in the [HTML block]:
2880
2881```````````````````````````````` example
2882<script>
2883foo
2884</script>1. *bar*
2885.
2886<script>
2887foo
2888</script>1. *bar*
2889````````````````````````````````
2890
2891
2892A comment (type 2):
2893
2894```````````````````````````````` example
2895<!-- Foo
2896
2897bar
2898 baz -->
2899okay
2900.
2901<!-- Foo
2902
2903bar
2904 baz -->
2905<p>okay</p>
2906````````````````````````````````
2907
2908
2909
2910A processing instruction (type 3):
2911
2912```````````````````````````````` example
2913<?php
2914
2915 echo '>';
2916
2917?>
2918okay
2919.
2920<?php
2921
2922 echo '>';
2923
2924?>
2925<p>okay</p>
2926````````````````````````````````
2927
2928
2929A declaration (type 4):
2930
2931```````````````````````````````` example
2932<!DOCTYPE html>
2933.
2934<!DOCTYPE html>
2935````````````````````````````````
2936
2937
2938CDATA (type 5):
2939
2940```````````````````````````````` example
2941<![CDATA[
2942function matchwo(a,b)
2943{
2944 if (a < b && a < 0) then {
2945 return 1;
2946
2947 } else {
2948
2949 return 0;
2950 }
2951}
2952]]>
2953okay
2954.
2955<![CDATA[
2956function matchwo(a,b)
2957{
2958 if (a < b && a < 0) then {
2959 return 1;
2960
2961 } else {
2962
2963 return 0;
2964 }
2965}
2966]]>
2967<p>okay</p>
2968````````````````````````````````
2969
2970
2971The opening tag can be preceded by up to three spaces of indentation, but not
2972four:
2973
2974```````````````````````````````` example
2975 <!-- foo -->
2976
2977 <!-- foo -->
2978.
2979 <!-- foo -->
2980<pre><code>&lt;!-- foo --&gt;
2981</code></pre>
2982````````````````````````````````
2983
2984
2985```````````````````````````````` example
2986 <div>
2987
2988 <div>
2989.
2990 <div>
2991<pre><code>&lt;div&gt;
2992</code></pre>
2993````````````````````````````````
2994
2995
2996An HTML block of types 1--6 can interrupt a paragraph, and need not be
2997preceded by a blank line.
2998
2999```````````````````````````````` example
3000Foo
3001<div>
3002bar
3003</div>
3004.
3005<p>Foo</p>
3006<div>
3007bar
3008</div>
3009````````````````````````````````
3010
3011
3012However, a following blank line is needed, except at the end of
3013a document, and except for blocks of types 1--5, [above][HTML
3014block]:
3015
3016```````````````````````````````` example
3017<div>
3018bar
3019</div>
3020*foo*
3021.
3022<div>
3023bar
3024</div>
3025*foo*
3026````````````````````````````````
3027
3028
3029HTML blocks of type 7 cannot interrupt a paragraph:
3030
3031```````````````````````````````` example
3032Foo
3033<a href="bar">
3034baz
3035.
3036<p>Foo
3037<a href="bar">
3038baz</p>
3039````````````````````````````````
3040
3041
3042This rule differs from John Gruber's original Markdown syntax
3043specification, which says:
3044
3045> The only restrictions are that block-level HTML elements —
3046> e.g. `<div>`, `<table>`, `<pre>`, `<p>`, etc. — must be separated from
3047> surrounding content by blank lines, and the start and end tags of the
3048> block should not be indented with spaces or tabs.
3049
3050In some ways Gruber's rule is more restrictive than the one given
3051here:
3052
3053- It requires that an HTML block be preceded by a blank line.
3054- It does not allow the start tag to be indented.
3055- It requires a matching end tag, which it also does not allow to
3056 be indented.
3057
3058Most Markdown implementations (including some of Gruber's own) do not
3059respect all of these restrictions.
3060
3061There is one respect, however, in which Gruber's rule is more liberal
3062than the one given here, since it allows blank lines to occur inside
3063an HTML block. There are two reasons for disallowing them here.
3064First, it removes the need to parse balanced tags, which is
3065expensive and can require backtracking from the end of the document
3066if no matching end tag is found. Second, it provides a very simple
3067and flexible way of including Markdown content inside HTML tags:
3068simply separate the Markdown from the HTML using blank lines:
3069
3070Compare:
3071
3072```````````````````````````````` example
3073<div>
3074
3075*Emphasized* text.
3076
3077</div>
3078.
3079<div>
3080<p><em>Emphasized</em> text.</p>
3081</div>
3082````````````````````````````````
3083
3084
3085```````````````````````````````` example
3086<div>
3087*Emphasized* text.
3088</div>
3089.
3090<div>
3091*Emphasized* text.
3092</div>
3093````````````````````````````````
3094
3095
3096Some Markdown implementations have adopted a convention of
3097interpreting content inside tags as text if the open tag has
3098the attribute `markdown=1`. The rule given above seems a simpler and
3099more elegant way of achieving the same expressive power, which is also
3100much simpler to parse.
3101
3102The main potential drawback is that one can no longer paste HTML
3103blocks into Markdown documents with 100% reliability. However,
3104*in most cases* this will work fine, because the blank lines in
3105HTML are usually followed by HTML block tags. For example:
3106
3107```````````````````````````````` example
3108<table>
3109
3110<tr>
3111
3112<td>
3113Hi
3114</td>
3115
3116</tr>
3117
3118</table>
3119.
3120<table>
3121<tr>
3122<td>
3123Hi
3124</td>
3125</tr>
3126</table>
3127````````````````````````````````
3128
3129
3130There are problems, however, if the inner tags are indented
3131*and* separated by spaces, as then they will be interpreted as
3132an indented code block:
3133
3134```````````````````````````````` example
3135<table>
3136
3137 <tr>
3138
3139 <td>
3140 Hi
3141 </td>
3142
3143 </tr>
3144
3145</table>
3146.
3147<table>
3148 <tr>
3149<pre><code>&lt;td&gt;
3150 Hi
3151&lt;/td&gt;
3152</code></pre>
3153 </tr>
3154</table>
3155````````````````````````````````
3156
3157
3158Fortunately, blank lines are usually not necessary and can be
3159deleted. The exception is inside `<pre>` tags, but as described
3160[above][HTML blocks], raw HTML blocks starting with `<pre>`
3161*can* contain blank lines.
3162
3163## Link reference definitions
3164
3165A [link reference definition](@)
3166consists of a [link label], optionally preceded by up to three spaces of
3167indentation, followed
3168by a colon (`:`), optional spaces or tabs (including up to one
3169[line ending]), a [link destination],
3170optional spaces or tabs (including up to one
3171[line ending]), and an optional [link
3172title], which if it is present must be separated
3173from the [link destination] by spaces or tabs.
3174No further character may occur.
3175
3176A [link reference definition]
3177does not correspond to a structural element of a document. Instead, it
3178defines a label which can be used in [reference links]
3179and reference-style [images] elsewhere in the document. [Link
3180reference definitions] can come either before or after the links that use
3181them.
3182
3183```````````````````````````````` example
3184[foo]: /url "title"
3185
3186[foo]
3187.
3188<p><a href="/url" title="title">foo</a></p>
3189````````````````````````````````
3190
3191
3192```````````````````````````````` example
3193 [foo]:
3194 /url
3195 'the title'
3196
3197[foo]
3198.
3199<p><a href="/url" title="the title">foo</a></p>
3200````````````````````````````````
3201
3202
3203```````````````````````````````` example
3204[Foo*bar\]]:my_(url) 'title (with parens)'
3205
3206[Foo*bar\]]
3207.
3208<p><a href="my_(url)" title="title (with parens)">Foo*bar]</a></p>
3209````````````````````````````````
3210
3211
3212```````````````````````````````` example
3213[Foo bar]:
3214<my url>
3215'title'
3216
3217[Foo bar]
3218.
3219<p><a href="my%20url" title="title">Foo bar</a></p>
3220````````````````````````````````
3221
3222
3223The title may extend over multiple lines:
3224
3225```````````````````````````````` example
3226[foo]: /url '
3227title
3228line1
3229line2
3230'
3231
3232[foo]
3233.
3234<p><a href="/url" title="
3235title
3236line1
3237line2
3238">foo</a></p>
3239````````````````````````````````
3240
3241
3242However, it may not contain a [blank line]:
3243
3244```````````````````````````````` example
3245[foo]: /url 'title
3246
3247with blank line'
3248
3249[foo]
3250.
3251<p>[foo]: /url 'title</p>
3252<p>with blank line'</p>
3253<p>[foo]</p>
3254````````````````````````````````
3255
3256
3257The title may be omitted:
3258
3259```````````````````````````````` example
3260[foo]:
3261/url
3262
3263[foo]
3264.
3265<p><a href="/url">foo</a></p>
3266````````````````````````````````
3267
3268
3269The link destination may not be omitted:
3270
3271```````````````````````````````` example
3272[foo]:
3273
3274[foo]
3275.
3276<p>[foo]:</p>
3277<p>[foo]</p>
3278````````````````````````````````
3279
3280 However, an empty link destination may be specified using
3281 angle brackets:
3282
3283```````````````````````````````` example
3284[foo]: <>
3285
3286[foo]
3287.
3288<p><a href="">foo</a></p>
3289````````````````````````````````
3290
3291The title must be separated from the link destination by
3292spaces or tabs:
3293
3294```````````````````````````````` example
3295[foo]: <bar>(baz)
3296
3297[foo]
3298.
3299<p>[foo]: <bar>(baz)</p>
3300<p>[foo]</p>
3301````````````````````````````````
3302
3303
3304Both title and destination can contain backslash escapes
3305and literal backslashes:
3306
3307```````````````````````````````` example
3308[foo]: /url\bar\*baz "foo\"bar\baz"
3309
3310[foo]
3311.
3312<p><a href="/url%5Cbar*baz" title="foo&quot;bar\baz">foo</a></p>
3313````````````````````````````````
3314
3315
3316A link can come before its corresponding definition:
3317
3318```````````````````````````````` example
3319[foo]
3320
3321[foo]: url
3322.
3323<p><a href="url">foo</a></p>
3324````````````````````````````````
3325
3326
3327If there are several matching definitions, the first one takes
3328precedence:
3329
3330```````````````````````````````` example
3331[foo]
3332
3333[foo]: first
3334[foo]: second
3335.
3336<p><a href="first">foo</a></p>
3337````````````````````````````````
3338
3339
3340As noted in the section on [Links], matching of labels is
3341case-insensitive (see [matches]).
3342
3343```````````````````````````````` example
3344[FOO]: /url
3345
3346[Foo]
3347.
3348<p><a href="/url">Foo</a></p>
3349````````````````````````````````
3350
3351
3352```````````````````````````````` example
3353[ΑΓΩ]: /φου
3354
3355[αγω]
3356.
3357<p><a href="/%CF%86%CE%BF%CF%85">αγω</a></p>
3358````````````````````````````````
3359
3360
3361Whether something is a [link reference definition] is
3362independent of whether the link reference it defines is
3363used in the document. Thus, for example, the following
3364document contains just a link reference definition, and
3365no visible content:
3366
3367```````````````````````````````` example
3368[foo]: /url
3369.
3370````````````````````````````````
3371
3372
3373Here is another one:
3374
3375```````````````````````````````` example
3376[
3377foo
3378]: /url
3379bar
3380.
3381<p>bar</p>
3382````````````````````````````````
3383
3384
3385This is not a link reference definition, because there are
3386characters other than spaces or tabs after the title:
3387
3388```````````````````````````````` example
3389[foo]: /url "title" ok
3390.
3391<p>[foo]: /url &quot;title&quot; ok</p>
3392````````````````````````````````
3393
3394
3395This is a link reference definition, but it has no title:
3396
3397```````````````````````````````` example
3398[foo]: /url
3399"title" ok
3400.
3401<p>&quot;title&quot; ok</p>
3402````````````````````````````````
3403
3404
3405This is not a link reference definition, because it is indented
3406four spaces:
3407
3408```````````````````````````````` example
3409 [foo]: /url "title"
3410
3411[foo]
3412.
3413<pre><code>[foo]: /url &quot;title&quot;
3414</code></pre>
3415<p>[foo]</p>
3416````````````````````````````````
3417
3418
3419This is not a link reference definition, because it occurs inside
3420a code block:
3421
3422```````````````````````````````` example
3423```
3424[foo]: /url
3425```
3426
3427[foo]
3428.
3429<pre><code>[foo]: /url
3430</code></pre>
3431<p>[foo]</p>
3432````````````````````````````````
3433
3434
3435A [link reference definition] cannot interrupt a paragraph.
3436
3437```````````````````````````````` example
3438Foo
3439[bar]: /baz
3440
3441[bar]
3442.
3443<p>Foo
3444[bar]: /baz</p>
3445<p>[bar]</p>
3446````````````````````````````````
3447
3448
3449However, it can directly follow other block elements, such as headings
3450and thematic breaks, and it need not be followed by a blank line.
3451
3452```````````````````````````````` example
3453# [Foo]
3454[foo]: /url
3455> bar
3456.
3457<h1><a href="/url">Foo</a></h1>
3458<blockquote>
3459<p>bar</p>
3460</blockquote>
3461````````````````````````````````
3462
3463```````````````````````````````` example
3464[foo]: /url
3465bar
3466===
3467[foo]
3468.
3469<h1>bar</h1>
3470<p><a href="/url">foo</a></p>
3471````````````````````````````````
3472
3473```````````````````````````````` example
3474[foo]: /url
3475===
3476[foo]
3477.
3478<p>===
3479<a href="/url">foo</a></p>
3480````````````````````````````````
3481
3482
3483Several [link reference definitions]
3484can occur one after another, without intervening blank lines.
3485
3486```````````````````````````````` example
3487[foo]: /foo-url "foo"
3488[bar]: /bar-url
3489 "bar"
3490[baz]: /baz-url
3491
3492[foo],
3493[bar],
3494[baz]
3495.
3496<p><a href="/foo-url" title="foo">foo</a>,
3497<a href="/bar-url" title="bar">bar</a>,
3498<a href="/baz-url">baz</a></p>
3499````````````````````````````````
3500
3501
3502[Link reference definitions] can occur
3503inside block containers, like lists and block quotations. They
3504affect the entire document, not just the container in which they
3505are defined:
3506
3507```````````````````````````````` example
3508[foo]
3509
3510> [foo]: /url
3511.
3512<p><a href="/url">foo</a></p>
3513<blockquote>
3514</blockquote>
3515````````````````````````````````
3516
3517
3518## Paragraphs
3519
3520A sequence of non-blank lines that cannot be interpreted as other
3521kinds of blocks forms a [paragraph](@).
3522The contents of the paragraph are the result of parsing the
3523paragraph's raw content as inlines. The paragraph's raw content
3524is formed by concatenating the lines and removing initial and final
3525spaces or tabs.
3526
3527A simple example with two paragraphs:
3528
3529```````````````````````````````` example
3530aaa
3531
3532bbb
3533.
3534<p>aaa</p>
3535<p>bbb</p>
3536````````````````````````````````
3537
3538
3539Paragraphs can contain multiple lines, but no blank lines:
3540
3541```````````````````````````````` example
3542aaa
3543bbb
3544
3545ccc
3546ddd
3547.
3548<p>aaa
3549bbb</p>
3550<p>ccc
3551ddd</p>
3552````````````````````````````````
3553
3554
3555Multiple blank lines between paragraphs have no effect:
3556
3557```````````````````````````````` example
3558aaa
3559
3560
3561bbb
3562.
3563<p>aaa</p>
3564<p>bbb</p>
3565````````````````````````````````
3566
3567
3568Leading spaces or tabs are skipped:
3569
3570```````````````````````````````` example
3571 aaa
3572 bbb
3573.
3574<p>aaa
3575bbb</p>
3576````````````````````````````````
3577
3578
3579Lines after the first may be indented any amount, since indented
3580code blocks cannot interrupt paragraphs.
3581
3582```````````````````````````````` example
3583aaa
3584 bbb
3585 ccc
3586.
3587<p>aaa
3588bbb
3589ccc</p>
3590````````````````````````````````
3591
3592
3593However, the first line may be preceded by up to three spaces of indentation.
3594Four spaces of indentation is too many:
3595
3596```````````````````````````````` example
3597 aaa
3598bbb
3599.
3600<p>aaa
3601bbb</p>
3602````````````````````````````````
3603
3604
3605```````````````````````````````` example
3606 aaa
3607bbb
3608.
3609<pre><code>aaa
3610</code></pre>
3611<p>bbb</p>
3612````````````````````````````````
3613
3614
3615Final spaces or tabs are stripped before inline parsing, so a paragraph
3616that ends with two or more spaces will not end with a [hard line
3617break]:
3618
3619```````````````````````````````` example
3620aaa
3621bbb
3622.
3623<p>aaa<br />
3624bbb</p>
3625````````````````````````````````
3626
3627
3628## Blank lines
3629
3630[Blank lines] between block-level elements are ignored,
3631except for the role they play in determining whether a [list]
3632is [tight] or [loose].
3633
3634Blank lines at the beginning and end of the document are also ignored.
3635
3636```````````````````````````````` example
3637
3638
3639aaa
3640
3641
3642# aaa
3643
3644
3645.
3646<p>aaa</p>
3647<h1>aaa</h1>
3648````````````````````````````````
3649
3650
3651
3652# Container blocks
3653
3654A [container block](#container-blocks) is a block that has other
3655blocks as its contents. There are two basic kinds of container blocks:
3656[block quotes] and [list items].
3657[Lists] are meta-containers for [list items].
3658
3659We define the syntax for container blocks recursively. The general
3660form of the definition is:
3661
3662> If X is a sequence of blocks, then the result of
3663> transforming X in such-and-such a way is a container of type Y
3664> with these blocks as its content.
3665
3666So, we explain what counts as a block quote or list item by explaining
3667how these can be *generated* from their contents. This should suffice
3668to define the syntax, although it does not give a recipe for *parsing*
3669these constructions. (A recipe is provided below in the section entitled
3670[A parsing strategy](#appendix-a-parsing-strategy).)
3671
3672## Block quotes
3673
3674A [block quote marker](@),
3675optionally preceded by up to three spaces of indentation,
3676consists of (a) the character `>` together with a following space of
3677indentation, or (b) a single character `>` not followed by a space of
3678indentation.
3679
3680The following rules define [block quotes]:
3681
36821. **Basic case.** If a string of lines *Ls* constitute a sequence
3683 of blocks *Bs*, then the result of prepending a [block quote
3684 marker] to the beginning of each line in *Ls*
3685 is a [block quote](#block-quotes) containing *Bs*.
3686
36872. **Laziness.** If a string of lines *Ls* constitute a [block
3688 quote](#block-quotes) with contents *Bs*, then the result of deleting
3689 the initial [block quote marker] from one or
3690 more lines in which the next character other than a space or tab after the
3691 [block quote marker] is [paragraph continuation
3692 text] is a block quote with *Bs* as its content.
3693 [Paragraph continuation text](@) is text
3694 that will be parsed as part of the content of a paragraph, but does
3695 not occur at the beginning of the paragraph.
3696
36973. **Consecutiveness.** A document cannot contain two [block
3698 quotes] in a row unless there is a [blank line] between them.
3699
3700Nothing else counts as a [block quote](#block-quotes).
3701
3702Here is a simple example:
3703
3704```````````````````````````````` example
3705> # Foo
3706> bar
3707> baz
3708.
3709<blockquote>
3710<h1>Foo</h1>
3711<p>bar
3712baz</p>
3713</blockquote>
3714````````````````````````````````
3715
3716
3717The space or tab after the `>` characters can be omitted:
3718
3719```````````````````````````````` example
3720># Foo
3721>bar
3722> baz
3723.
3724<blockquote>
3725<h1>Foo</h1>
3726<p>bar
3727baz</p>
3728</blockquote>
3729````````````````````````````````
3730
3731
3732The `>` characters can be preceded by up to three spaces of indentation:
3733
3734```````````````````````````````` example
3735 > # Foo
3736 > bar
3737 > baz
3738.
3739<blockquote>
3740<h1>Foo</h1>
3741<p>bar
3742baz</p>
3743</blockquote>
3744````````````````````````````````
3745
3746
3747Four spaces of indentation is too many:
3748
3749```````````````````````````````` example
3750 > # Foo
3751 > bar
3752 > baz
3753.
3754<pre><code>&gt; # Foo
3755&gt; bar
3756&gt; baz
3757</code></pre>
3758````````````````````````````````
3759
3760
3761The Laziness clause allows us to omit the `>` before
3762[paragraph continuation text]:
3763
3764```````````````````````````````` example
3765> # Foo
3766> bar
3767baz
3768.
3769<blockquote>
3770<h1>Foo</h1>
3771<p>bar
3772baz</p>
3773</blockquote>
3774````````````````````````````````
3775
3776
3777A block quote can contain some lazy and some non-lazy
3778continuation lines:
3779
3780```````````````````````````````` example
3781> bar
3782baz
3783> foo
3784.
3785<blockquote>
3786<p>bar
3787baz
3788foo</p>
3789</blockquote>
3790````````````````````````````````
3791
3792
3793Laziness only applies to lines that would have been continuations of
3794paragraphs had they been prepended with [block quote markers].
3795For example, the `> ` cannot be omitted in the second line of
3796
3797``` markdown
3798> foo
3799> ---
3800```
3801
3802without changing the meaning:
3803
3804```````````````````````````````` example
3805> foo
3806---
3807.
3808<blockquote>
3809<p>foo</p>
3810</blockquote>
3811<hr />
3812````````````````````````````````
3813
3814
3815Similarly, if we omit the `> ` in the second line of
3816
3817``` markdown
3818> - foo
3819> - bar
3820```
3821
3822then the block quote ends after the first line:
3823
3824```````````````````````````````` example
3825> - foo
3826- bar
3827.
3828<blockquote>
3829<ul>
3830<li>foo</li>
3831</ul>
3832</blockquote>
3833<ul>
3834<li>bar</li>
3835</ul>
3836````````````````````````````````
3837
3838
3839For the same reason, we can't omit the `> ` in front of
3840subsequent lines of an indented or fenced code block:
3841
3842```````````````````````````````` example
3843> foo
3844 bar
3845.
3846<blockquote>
3847<pre><code>foo
3848</code></pre>
3849</blockquote>
3850<pre><code>bar
3851</code></pre>
3852````````````````````````````````
3853
3854
3855```````````````````````````````` example
3856> ```
3857foo
3858```
3859.
3860<blockquote>
3861<pre><code></code></pre>
3862</blockquote>
3863<p>foo</p>
3864<pre><code></code></pre>
3865````````````````````````````````
3866
3867
3868Note that in the following case, we have a [lazy
3869continuation line]:
3870
3871```````````````````````````````` example
3872> foo
3873 - bar
3874.
3875<blockquote>
3876<p>foo
3877- bar</p>
3878</blockquote>
3879````````````````````````````````
3880
3881
3882To see why, note that in
3883
3884```markdown
3885> foo
3886> - bar
3887```
3888
3889the `- bar` is indented too far to start a list, and can't
3890be an indented code block because indented code blocks cannot
3891interrupt paragraphs, so it is [paragraph continuation text].
3892
3893A block quote can be empty:
3894
3895```````````````````````````````` example
3896>
3897.
3898<blockquote>
3899</blockquote>
3900````````````````````````````````
3901
3902
3903```````````````````````````````` example
3904>
3905>
3906>
3907.
3908<blockquote>
3909</blockquote>
3910````````````````````````````````
3911
3912
3913A block quote can have initial or final blank lines:
3914
3915```````````````````````````````` example
3916>
3917> foo
3918>
3919.
3920<blockquote>
3921<p>foo</p>
3922</blockquote>
3923````````````````````````````````
3924
3925
3926A blank line always separates block quotes:
3927
3928```````````````````````````````` example
3929> foo
3930
3931> bar
3932.
3933<blockquote>
3934<p>foo</p>
3935</blockquote>
3936<blockquote>
3937<p>bar</p>
3938</blockquote>
3939````````````````````````````````
3940
3941
3942(Most current Markdown implementations, including John Gruber's
3943original `Markdown.pl`, will parse this example as a single block quote
3944with two paragraphs. But it seems better to allow the author to decide
3945whether two block quotes or one are wanted.)
3946
3947Consecutiveness means that if we put these block quotes together,
3948we get a single block quote:
3949
3950```````````````````````````````` example
3951> foo
3952> bar
3953.
3954<blockquote>
3955<p>foo
3956bar</p>
3957</blockquote>
3958````````````````````````````````
3959
3960
3961To get a block quote with two paragraphs, use:
3962
3963```````````````````````````````` example
3964> foo
3965>
3966> bar
3967.
3968<blockquote>
3969<p>foo</p>
3970<p>bar</p>
3971</blockquote>
3972````````````````````````````````
3973
3974
3975Block quotes can interrupt paragraphs:
3976
3977```````````````````````````````` example
3978foo
3979> bar
3980.
3981<p>foo</p>
3982<blockquote>
3983<p>bar</p>
3984</blockquote>
3985````````````````````````````````
3986
3987
3988In general, blank lines are not needed before or after block
3989quotes:
3990
3991```````````````````````````````` example
3992> aaa
3993***
3994> bbb
3995.
3996<blockquote>
3997<p>aaa</p>
3998</blockquote>
3999<hr />
4000<blockquote>
4001<p>bbb</p>
4002</blockquote>
4003````````````````````````````````
4004
4005
4006However, because of laziness, a blank line is needed between
4007a block quote and a following paragraph:
4008
4009```````````````````````````````` example
4010> bar
4011baz
4012.
4013<blockquote>
4014<p>bar
4015baz</p>
4016</blockquote>
4017````````````````````````````````
4018
4019
4020```````````````````````````````` example
4021> bar
4022
4023baz
4024.
4025<blockquote>
4026<p>bar</p>
4027</blockquote>
4028<p>baz</p>
4029````````````````````````````````
4030
4031
4032```````````````````````````````` example
4033> bar
4034>
4035baz
4036.
4037<blockquote>
4038<p>bar</p>
4039</blockquote>
4040<p>baz</p>
4041````````````````````````````````
4042
4043
4044It is a consequence of the Laziness rule that any number
4045of initial `>`s may be omitted on a continuation line of a
4046nested block quote:
4047
4048```````````````````````````````` example
4049> > > foo
4050bar
4051.
4052<blockquote>
4053<blockquote>
4054<blockquote>
4055<p>foo
4056bar</p>
4057</blockquote>
4058</blockquote>
4059</blockquote>
4060````````````````````````````````
4061
4062
4063```````````````````````````````` example
4064>>> foo
4065> bar
4066>>baz
4067.
4068<blockquote>
4069<blockquote>
4070<blockquote>
4071<p>foo
4072bar
4073baz</p>
4074</blockquote>
4075</blockquote>
4076</blockquote>
4077````````````````````````````````
4078
4079
4080When including an indented code block in a block quote,
4081remember that the [block quote marker] includes
4082both the `>` and a following space of indentation. So *five spaces* are needed
4083after the `>`:
4084
4085```````````````````````````````` example
4086> code
4087
4088> not code
4089.
4090<blockquote>
4091<pre><code>code
4092</code></pre>
4093</blockquote>
4094<blockquote>
4095<p>not code</p>
4096</blockquote>
4097````````````````````````````````
4098
4099
4100
4101## List items
4102
4103A [list marker](@) is a
4104[bullet list marker] or an [ordered list marker].
4105
4106A [bullet list marker](@)
4107is a `-`, `+`, or `*` character.
4108
4109An [ordered list marker](@)
4110is a sequence of 1--9 arabic digits (`0-9`), followed by either a
4111`.` character or a `)` character. (The reason for the length
4112limit is that with 10 digits we start seeing integer overflows
4113in some browsers.)
4114
4115The following rules define [list items]:
4116
41171. **Basic case.** If a sequence of lines *Ls* constitute a sequence of
4118 blocks *Bs* starting with a character other than a space or tab, and *M* is
4119 a list marker of width *W* followed by 1 ≤ *N* ≤ 4 spaces of indentation,
4120 then the result of prepending *M* and the following spaces to the first line
4121 of Ls*, and indenting subsequent lines of *Ls* by *W + N* spaces, is a
4122 list item with *Bs* as its contents. The type of the list item
4123 (bullet or ordered) is determined by the type of its list marker.
4124 If the list item is ordered, then it is also assigned a start
4125 number, based on the ordered list marker.
4126
4127 Exceptions:
4128
4129 1. When the first list item in a [list] interrupts
4130 a paragraph---that is, when it starts on a line that would
4131 otherwise count as [paragraph continuation text]---then (a)
4132 the lines *Ls* must not begin with a blank line, and (b) if
4133 the list item is ordered, the start number must be 1.
4134 2. If any line is a [thematic break][thematic breaks] then
4135 that line is not a list item.
4136
4137For example, let *Ls* be the lines
4138
4139```````````````````````````````` example
4140A paragraph
4141with two lines.
4142
4143 indented code
4144
4145> A block quote.
4146.
4147<p>A paragraph
4148with two lines.</p>
4149<pre><code>indented code
4150</code></pre>
4151<blockquote>
4152<p>A block quote.</p>
4153</blockquote>
4154````````````````````````````````
4155
4156
4157And let *M* be the marker `1.`, and *N* = 2. Then rule #1 says
4158that the following is an ordered list item with start number 1,
4159and the same contents as *Ls*:
4160
4161```````````````````````````````` example
41621. A paragraph
4163 with two lines.
4164
4165 indented code
4166
4167 > A block quote.
4168.
4169<ol>
4170<li>
4171<p>A paragraph
4172with two lines.</p>
4173<pre><code>indented code
4174</code></pre>
4175<blockquote>
4176<p>A block quote.</p>
4177</blockquote>
4178</li>
4179</ol>
4180````````````````````````````````
4181
4182
4183The most important thing to notice is that the position of
4184the text after the list marker determines how much indentation
4185is needed in subsequent blocks in the list item. If the list
4186marker takes up two spaces of indentation, and there are three spaces between
4187the list marker and the next character other than a space or tab, then blocks
4188must be indented five spaces in order to fall under the list
4189item.
4190
4191Here are some examples showing how far content must be indented to be
4192put under the list item:
4193
4194```````````````````````````````` example
4195- one
4196
4197 two
4198.
4199<ul>
4200<li>one</li>
4201</ul>
4202<p>two</p>
4203````````````````````````````````
4204
4205
4206```````````````````````````````` example
4207- one
4208
4209 two
4210.
4211<ul>
4212<li>
4213<p>one</p>
4214<p>two</p>
4215</li>
4216</ul>
4217````````````````````````````````
4218
4219
4220```````````````````````````````` example
4221 - one
4222
4223 two
4224.
4225<ul>
4226<li>one</li>
4227</ul>
4228<pre><code> two
4229</code></pre>
4230````````````````````````````````
4231
4232
4233```````````````````````````````` example
4234 - one
4235
4236 two
4237.
4238<ul>
4239<li>
4240<p>one</p>
4241<p>two</p>
4242</li>
4243</ul>
4244````````````````````````````````
4245
4246
4247It is tempting to think of this in terms of columns: the continuation
4248blocks must be indented at least to the column of the first character other than
4249a space or tab after the list marker. However, that is not quite right.
4250The spaces of indentation after the list marker determine how much relative
4251indentation is needed. Which column this indentation reaches will depend on
4252how the list item is embedded in other constructions, as shown by
4253this example:
4254
4255```````````````````````````````` example
4256 > > 1. one
4257>>
4258>> two
4259.
4260<blockquote>
4261<blockquote>
4262<ol>
4263<li>
4264<p>one</p>
4265<p>two</p>
4266</li>
4267</ol>
4268</blockquote>
4269</blockquote>
4270````````````````````````````````
4271
4272
4273Here `two` occurs in the same column as the list marker `1.`,
4274but is actually contained in the list item, because there is
4275sufficient indentation after the last containing blockquote marker.
4276
4277The converse is also possible. In the following example, the word `two`
4278occurs far to the right of the initial text of the list item, `one`, but
4279it is not considered part of the list item, because it is not indented
4280far enough past the blockquote marker:
4281
4282```````````````````````````````` example
4283>>- one
4284>>
4285 > > two
4286.
4287<blockquote>
4288<blockquote>
4289<ul>
4290<li>one</li>
4291</ul>
4292<p>two</p>
4293</blockquote>
4294</blockquote>
4295````````````````````````````````
4296
4297
4298Note that at least one space or tab is needed between the list marker and
4299any following content, so these are not list items:
4300
4301```````````````````````````````` example
4302-one
4303
43042.two
4305.
4306<p>-one</p>
4307<p>2.two</p>
4308````````````````````````````````
4309
4310
4311A list item may contain blocks that are separated by more than
4312one blank line.
4313
4314```````````````````````````````` example
4315- foo
4316
4317
4318 bar
4319.
4320<ul>
4321<li>
4322<p>foo</p>
4323<p>bar</p>
4324</li>
4325</ul>
4326````````````````````````````````
4327
4328
4329A list item may contain any kind of block:
4330
4331```````````````````````````````` example
43321. foo
4333
4334 ```
4335 bar
4336 ```
4337
4338 baz
4339
4340 > bam
4341.
4342<ol>
4343<li>
4344<p>foo</p>
4345<pre><code>bar
4346</code></pre>
4347<p>baz</p>
4348<blockquote>
4349<p>bam</p>
4350</blockquote>
4351</li>
4352</ol>
4353````````````````````````````````
4354
4355
4356A list item that contains an indented code block will preserve
4357empty lines within the code block verbatim.
4358
4359```````````````````````````````` example
4360- Foo
4361
4362 bar
4363
4364
4365 baz
4366.
4367<ul>
4368<li>
4369<p>Foo</p>
4370<pre><code>bar
4371
4372
4373baz
4374</code></pre>
4375</li>
4376</ul>
4377````````````````````````````````
4378
4379Note that ordered list start numbers must be nine digits or less:
4380
4381```````````````````````````````` example
4382123456789. ok
4383.
4384<ol start="123456789">
4385<li>ok</li>
4386</ol>
4387````````````````````````````````
4388
4389
4390```````````````````````````````` example
43911234567890. not ok
4392.
4393<p>1234567890. not ok</p>
4394````````````````````````````````
4395
4396
4397A start number may begin with 0s:
4398
4399```````````````````````````````` example
44000. ok
4401.
4402<ol start="0">
4403<li>ok</li>
4404</ol>
4405````````````````````````````````
4406
4407
4408```````````````````````````````` example
4409003. ok
4410.
4411<ol start="3">
4412<li>ok</li>
4413</ol>
4414````````````````````````````````
4415
4416
4417A start number may not be negative:
4418
4419```````````````````````````````` example
4420-1. not ok
4421.
4422<p>-1. not ok</p>
4423````````````````````````````````
4424
4425
4426
44272. **Item starting with indented code.** If a sequence of lines *Ls*
4428 constitute a sequence of blocks *Bs* starting with an indented code
4429 block, and *M* is a list marker of width *W* followed by
4430 one space of indentation, then the result of prepending *M* and the
4431 following space to the first line of *Ls*, and indenting subsequent lines
4432 of *Ls* by *W + 1* spaces, is a list item with *Bs* as its contents.
4433 If a line is empty, then it need not be indented. The type of the
4434 list item (bullet or ordered) is determined by the type of its list
4435 marker. If the list item is ordered, then it is also assigned a
4436 start number, based on the ordered list marker.
4437
4438An indented code block will have to be preceded by four spaces of indentation
4439beyond the edge of the region where text will be included in the list item.
4440In the following case that is 6 spaces:
4441
4442```````````````````````````````` example
4443- foo
4444
4445 bar
4446.
4447<ul>
4448<li>
4449<p>foo</p>
4450<pre><code>bar
4451</code></pre>
4452</li>
4453</ul>
4454````````````````````````````````
4455
4456
4457And in this case it is 11 spaces:
4458
4459```````````````````````````````` example
4460 10. foo
4461
4462 bar
4463.
4464<ol start="10">
4465<li>
4466<p>foo</p>
4467<pre><code>bar
4468</code></pre>
4469</li>
4470</ol>
4471````````````````````````````````
4472
4473
4474If the *first* block in the list item is an indented code block,
4475then by rule #2, the contents must be preceded by *one* space of indentation
4476after the list marker:
4477
4478```````````````````````````````` example
4479 indented code
4480
4481paragraph
4482
4483 more code
4484.
4485<pre><code>indented code
4486</code></pre>
4487<p>paragraph</p>
4488<pre><code>more code
4489</code></pre>
4490````````````````````````````````
4491
4492
4493```````````````````````````````` example
44941. indented code
4495
4496 paragraph
4497
4498 more code
4499.
4500<ol>
4501<li>
4502<pre><code>indented code
4503</code></pre>
4504<p>paragraph</p>
4505<pre><code>more code
4506</code></pre>
4507</li>
4508</ol>
4509````````````````````````````````
4510
4511
4512Note that an additional space of indentation is interpreted as space
4513inside the code block:
4514
4515```````````````````````````````` example
45161. indented code
4517
4518 paragraph
4519
4520 more code
4521.
4522<ol>
4523<li>
4524<pre><code> indented code
4525</code></pre>
4526<p>paragraph</p>
4527<pre><code>more code
4528</code></pre>
4529</li>
4530</ol>
4531````````````````````````````````
4532
4533
4534Note that rules #1 and #2 only apply to two cases: (a) cases
4535in which the lines to be included in a list item begin with a
4536characer other than a space or tab, and (b) cases in which
4537they begin with an indented code
4538block. In a case like the following, where the first block begins with
4539three spaces of indentation, the rules do not allow us to form a list item by
4540indenting the whole thing and prepending a list marker:
4541
4542```````````````````````````````` example
4543 foo
4544
4545bar
4546.
4547<p>foo</p>
4548<p>bar</p>
4549````````````````````````````````
4550
4551
4552```````````````````````````````` example
4553- foo
4554
4555 bar
4556.
4557<ul>
4558<li>foo</li>
4559</ul>
4560<p>bar</p>
4561````````````````````````````````
4562
4563
4564This is not a significant restriction, because when a block is preceded by up to
4565three spaces of indentation, the indentation can always be removed without
4566a change in interpretation, allowing rule #1 to be applied. So, in
4567the above case:
4568
4569```````````````````````````````` example
4570- foo
4571
4572 bar
4573.
4574<ul>
4575<li>
4576<p>foo</p>
4577<p>bar</p>
4578</li>
4579</ul>
4580````````````````````````````````
4581
4582
45833. **Item starting with a blank line.** If a sequence of lines *Ls*
4584 starting with a single [blank line] constitute a (possibly empty)
4585 sequence of blocks *Bs*, and *M* is a list marker of width *W*,
4586 then the result of prepending *M* to the first line of *Ls*, and
4587 preceding subsequent lines of *Ls* by *W + 1* spaces of indentation, is a
4588 list item with *Bs* as its contents.
4589 If a line is empty, then it need not be indented. The type of the
4590 list item (bullet or ordered) is determined by the type of its list
4591 marker. If the list item is ordered, then it is also assigned a
4592 start number, based on the ordered list marker.
4593
4594Here are some list items that start with a blank line but are not empty:
4595
4596```````````````````````````````` example
4597-
4598 foo
4599-
4600 ```
4601 bar
4602 ```
4603-
4604 baz
4605.
4606<ul>
4607<li>foo</li>
4608<li>
4609<pre><code>bar
4610</code></pre>
4611</li>
4612<li>
4613<pre><code>baz
4614</code></pre>
4615</li>
4616</ul>
4617````````````````````````````````
4618
4619When the list item starts with a blank line, the number of spaces
4620following the list marker doesn't change the required indentation:
4621
4622```````````````````````````````` example
4623-
4624 foo
4625.
4626<ul>
4627<li>foo</li>
4628</ul>
4629````````````````````````````````
4630
4631
4632A list item can begin with at most one blank line.
4633In the following example, `foo` is not part of the list
4634item:
4635
4636```````````````````````````````` example
4637-
4638
4639 foo
4640.
4641<ul>
4642<li></li>
4643</ul>
4644<p>foo</p>
4645````````````````````````````````
4646
4647
4648Here is an empty bullet list item:
4649
4650```````````````````````````````` example
4651- foo
4652-
4653- bar
4654.
4655<ul>
4656<li>foo</li>
4657<li></li>
4658<li>bar</li>
4659</ul>
4660````````````````````````````````
4661
4662
4663It does not matter whether there are spaces or tabs following the [list marker]:
4664
4665```````````````````````````````` example
4666- foo
4667-
4668- bar
4669.
4670<ul>
4671<li>foo</li>
4672<li></li>
4673<li>bar</li>
4674</ul>
4675````````````````````````````````
4676
4677
4678Here is an empty ordered list item:
4679
4680```````````````````````````````` example
46811. foo
46822.
46833. bar
4684.
4685<ol>
4686<li>foo</li>
4687<li></li>
4688<li>bar</li>
4689</ol>
4690````````````````````````````````
4691
4692
4693A list may start or end with an empty list item:
4694
4695```````````````````````````````` example
4696*
4697.
4698<ul>
4699<li></li>
4700</ul>
4701````````````````````````````````
4702
4703However, an empty list item cannot interrupt a paragraph:
4704
4705```````````````````````````````` example
4706foo
4707*
4708
4709foo
47101.
4711.
4712<p>foo
4713*</p>
4714<p>foo
47151.</p>
4716````````````````````````````````
4717
4718
47194. **Indentation.** If a sequence of lines *Ls* constitutes a list item
4720 according to rule #1, #2, or #3, then the result of preceding each line
4721 of *Ls* by up to three spaces of indentation (the same for each line) also
4722 constitutes a list item with the same contents and attributes. If a line is
4723 empty, then it need not be indented.
4724
4725Indented one space:
4726
4727```````````````````````````````` example
4728 1. A paragraph
4729 with two lines.
4730
4731 indented code
4732
4733 > A block quote.
4734.
4735<ol>
4736<li>
4737<p>A paragraph
4738with two lines.</p>
4739<pre><code>indented code
4740</code></pre>
4741<blockquote>
4742<p>A block quote.</p>
4743</blockquote>
4744</li>
4745</ol>
4746````````````````````````````````
4747
4748
4749Indented two spaces:
4750
4751```````````````````````````````` example
4752 1. A paragraph
4753 with two lines.
4754
4755 indented code
4756
4757 > A block quote.
4758.
4759<ol>
4760<li>
4761<p>A paragraph
4762with two lines.</p>
4763<pre><code>indented code
4764</code></pre>
4765<blockquote>
4766<p>A block quote.</p>
4767</blockquote>
4768</li>
4769</ol>
4770````````````````````````````````
4771
4772
4773Indented three spaces:
4774
4775```````````````````````````````` example
4776 1. A paragraph
4777 with two lines.
4778
4779 indented code
4780
4781 > A block quote.
4782.
4783<ol>
4784<li>
4785<p>A paragraph
4786with two lines.</p>
4787<pre><code>indented code
4788</code></pre>
4789<blockquote>
4790<p>A block quote.</p>
4791</blockquote>
4792</li>
4793</ol>
4794````````````````````````````````
4795
4796
4797Four spaces indent gives a code block:
4798
4799```````````````````````````````` example
4800 1. A paragraph
4801 with two lines.
4802
4803 indented code
4804
4805 > A block quote.
4806.
4807<pre><code>1. A paragraph
4808 with two lines.
4809
4810 indented code
4811
4812 &gt; A block quote.
4813</code></pre>
4814````````````````````````````````
4815
4816
4817
48185. **Laziness.** If a string of lines *Ls* constitute a [list
4819 item](#list-items) with contents *Bs*, then the result of deleting
4820 some or all of the indentation from one or more lines in which the
4821 next character other than a space or tab after the indentation is
4822 [paragraph continuation text] is a
4823 list item with the same contents and attributes. The unindented
4824 lines are called
4825 [lazy continuation line](@)s.
4826
4827Here is an example with [lazy continuation lines]:
4828
4829```````````````````````````````` example
4830 1. A paragraph
4831with two lines.
4832
4833 indented code
4834
4835 > A block quote.
4836.
4837<ol>
4838<li>
4839<p>A paragraph
4840with two lines.</p>
4841<pre><code>indented code
4842</code></pre>
4843<blockquote>
4844<p>A block quote.</p>
4845</blockquote>
4846</li>
4847</ol>
4848````````````````````````````````
4849
4850
4851Indentation can be partially deleted:
4852
4853```````````````````````````````` example
4854 1. A paragraph
4855 with two lines.
4856.
4857<ol>
4858<li>A paragraph
4859with two lines.</li>
4860</ol>
4861````````````````````````````````
4862
4863
4864These examples show how laziness can work in nested structures:
4865
4866```````````````````````````````` example
4867> 1. > Blockquote
4868continued here.
4869.
4870<blockquote>
4871<ol>
4872<li>
4873<blockquote>
4874<p>Blockquote
4875continued here.</p>
4876</blockquote>
4877</li>
4878</ol>
4879</blockquote>
4880````````````````````````````````
4881
4882
4883```````````````````````````````` example
4884> 1. > Blockquote
4885> continued here.
4886.
4887<blockquote>
4888<ol>
4889<li>
4890<blockquote>
4891<p>Blockquote
4892continued here.</p>
4893</blockquote>
4894</li>
4895</ol>
4896</blockquote>
4897````````````````````````````````
4898
4899
4900
49016. **That's all.** Nothing that is not counted as a list item by rules
4902 #1--5 counts as a [list item](#list-items).
4903
4904The rules for sublists follow from the general rules
4905[above][List items]. A sublist must be indented the same number
4906of spaces of indentation a paragraph would need to be in order to be included
4907in the list item.
4908
4909So, in this case we need two spaces indent:
4910
4911```````````````````````````````` example
4912- foo
4913 - bar
4914 - baz
4915 - boo
4916.
4917<ul>
4918<li>foo
4919<ul>
4920<li>bar
4921<ul>
4922<li>baz
4923<ul>
4924<li>boo</li>
4925</ul>
4926</li>
4927</ul>
4928</li>
4929</ul>
4930</li>
4931</ul>
4932````````````````````````````````
4933
4934
4935One is not enough:
4936
4937```````````````````````````````` example
4938- foo
4939 - bar
4940 - baz
4941 - boo
4942.
4943<ul>
4944<li>foo</li>
4945<li>bar</li>
4946<li>baz</li>
4947<li>boo</li>
4948</ul>
4949````````````````````````````````
4950
4951
4952Here we need four, because the list marker is wider:
4953
4954```````````````````````````````` example
495510) foo
4956 - bar
4957.
4958<ol start="10">
4959<li>foo
4960<ul>
4961<li>bar</li>
4962</ul>
4963</li>
4964</ol>
4965````````````````````````````````
4966
4967
4968Three is not enough:
4969
4970```````````````````````````````` example
497110) foo
4972 - bar
4973.
4974<ol start="10">
4975<li>foo</li>
4976</ol>
4977<ul>
4978<li>bar</li>
4979</ul>
4980````````````````````````````````
4981
4982
4983A list may be the first block in a list item:
4984
4985```````````````````````````````` example
4986- - foo
4987.
4988<ul>
4989<li>
4990<ul>
4991<li>foo</li>
4992</ul>
4993</li>
4994</ul>
4995````````````````````````````````
4996
4997
4998```````````````````````````````` example
49991. - 2. foo
5000.
5001<ol>
5002<li>
5003<ul>
5004<li>
5005<ol start="2">
5006<li>foo</li>
5007</ol>
5008</li>
5009</ul>
5010</li>
5011</ol>
5012````````````````````````````````
5013
5014
5015A list item can contain a heading:
5016
5017```````````````````````````````` example
5018- # Foo
5019- Bar
5020 ---
5021 baz
5022.
5023<ul>
5024<li>
5025<h1>Foo</h1>
5026</li>
5027<li>
5028<h2>Bar</h2>
5029baz</li>
5030</ul>
5031````````````````````````````````
5032
5033
5034### Motivation
5035
5036John Gruber's Markdown spec says the following about list items:
5037
50381. "List markers typically start at the left margin, but may be indented
5039 by up to three spaces. List markers must be followed by one or more
5040 spaces or a tab."
5041
50422. "To make lists look nice, you can wrap items with hanging indents....
5043 But if you don't want to, you don't have to."
5044
50453. "List items may consist of multiple paragraphs. Each subsequent
5046 paragraph in a list item must be indented by either 4 spaces or one
5047 tab."
5048
50494. "It looks nice if you indent every line of the subsequent paragraphs,
5050 but here again, Markdown will allow you to be lazy."
5051
50525. "To put a blockquote within a list item, the blockquote's `>`
5053 delimiters need to be indented."
5054
50556. "To put a code block within a list item, the code block needs to be
5056 indented twice — 8 spaces or two tabs."
5057
5058These rules specify that a paragraph under a list item must be indented
5059four spaces (presumably, from the left margin, rather than the start of
5060the list marker, but this is not said), and that code under a list item
5061must be indented eight spaces instead of the usual four. They also say
5062that a block quote must be indented, but not by how much; however, the
5063example given has four spaces indentation. Although nothing is said
5064about other kinds of block-level content, it is certainly reasonable to
5065infer that *all* block elements under a list item, including other
5066lists, must be indented four spaces. This principle has been called the
5067*four-space rule*.
5068
5069The four-space rule is clear and principled, and if the reference
5070implementation `Markdown.pl` had followed it, it probably would have
5071become the standard. However, `Markdown.pl` allowed paragraphs and
5072sublists to start with only two spaces indentation, at least on the
5073outer level. Worse, its behavior was inconsistent: a sublist of an
5074outer-level list needed two spaces indentation, but a sublist of this
5075sublist needed three spaces. It is not surprising, then, that different
5076implementations of Markdown have developed very different rules for
5077determining what comes under a list item. (Pandoc and python-Markdown,
5078for example, stuck with Gruber's syntax description and the four-space
5079rule, while discount, redcarpet, marked, PHP Markdown, and others
5080followed `Markdown.pl`'s behavior more closely.)
5081
5082Unfortunately, given the divergences between implementations, there
5083is no way to give a spec for list items that will be guaranteed not
5084to break any existing documents. However, the spec given here should
5085correctly handle lists formatted with either the four-space rule or
5086the more forgiving `Markdown.pl` behavior, provided they are laid out
5087in a way that is natural for a human to read.
5088
5089The strategy here is to let the width and indentation of the list marker
5090determine the indentation necessary for blocks to fall under the list
5091item, rather than having a fixed and arbitrary number. The writer can
5092think of the body of the list item as a unit which gets indented to the
5093right enough to fit the list marker (and any indentation on the list
5094marker). (The laziness rule, #5, then allows continuation lines to be
5095unindented if needed.)
5096
5097This rule is superior, we claim, to any rule requiring a fixed level of
5098indentation from the margin. The four-space rule is clear but
5099unnatural. It is quite unintuitive that
5100
5101``` markdown
5102- foo
5103
5104 bar
5105
5106 - baz
5107```
5108
5109should be parsed as two lists with an intervening paragraph,
5110
5111``` html
5112<ul>
5113<li>foo</li>
5114</ul>
5115<p>bar</p>
5116<ul>
5117<li>baz</li>
5118</ul>
5119```
5120
5121as the four-space rule demands, rather than a single list,
5122
5123``` html
5124<ul>
5125<li>
5126<p>foo</p>
5127<p>bar</p>
5128<ul>
5129<li>baz</li>
5130</ul>
5131</li>
5132</ul>
5133```
5134
5135The choice of four spaces is arbitrary. It can be learned, but it is
5136not likely to be guessed, and it trips up beginners regularly.
5137
5138Would it help to adopt a two-space rule? The problem is that such
5139a rule, together with the rule allowing up to three spaces of indentation for
5140the initial list marker, allows text that is indented *less than* the
5141original list marker to be included in the list item. For example,
5142`Markdown.pl` parses
5143
5144``` markdown
5145 - one
5146
5147 two
5148```
5149
5150as a single list item, with `two` a continuation paragraph:
5151
5152``` html
5153<ul>
5154<li>
5155<p>one</p>
5156<p>two</p>
5157</li>
5158</ul>
5159```
5160
5161and similarly
5162
5163``` markdown
5164> - one
5165>
5166> two
5167```
5168
5169as
5170
5171``` html
5172<blockquote>
5173<ul>
5174<li>
5175<p>one</p>
5176<p>two</p>
5177</li>
5178</ul>
5179</blockquote>
5180```
5181
5182This is extremely unintuitive.
5183
5184Rather than requiring a fixed indent from the margin, we could require
5185a fixed indent (say, two spaces, or even one space) from the list marker (which
5186may itself be indented). This proposal would remove the last anomaly
5187discussed. Unlike the spec presented above, it would count the following
5188as a list item with a subparagraph, even though the paragraph `bar`
5189is not indented as far as the first paragraph `foo`:
5190
5191``` markdown
5192 10. foo
5193
5194 bar
5195```
5196
5197Arguably this text does read like a list item with `bar` as a subparagraph,
5198which may count in favor of the proposal. However, on this proposal indented
5199code would have to be indented six spaces after the list marker. And this
5200would break a lot of existing Markdown, which has the pattern:
5201
5202``` markdown
52031. foo
5204
5205 indented code
5206```
5207
5208where the code is indented eight spaces. The spec above, by contrast, will
5209parse this text as expected, since the code block's indentation is measured
5210from the beginning of `foo`.
5211
5212The one case that needs special treatment is a list item that *starts*
5213with indented code. How much indentation is required in that case, since
5214we don't have a "first paragraph" to measure from? Rule #2 simply stipulates
5215that in such cases, we require one space indentation from the list marker
5216(and then the normal four spaces for the indented code). This will match the
5217four-space rule in cases where the list marker plus its initial indentation
5218takes four spaces (a common case), but diverge in other cases.
5219
5220## Lists
5221
5222A [list](@) is a sequence of one or more
5223list items [of the same type]. The list items
5224may be separated by any number of blank lines.
5225
5226Two list items are [of the same type](@)
5227if they begin with a [list marker] of the same type.
5228Two list markers are of the
5229same type if (a) they are bullet list markers using the same character
5230(`-`, `+`, or `*`) or (b) they are ordered list numbers with the same
5231delimiter (either `.` or `)`).
5232
5233A list is an [ordered list](@)
5234if its constituent list items begin with
5235[ordered list markers], and a
5236[bullet list](@) if its constituent list
5237items begin with [bullet list markers].
5238
5239The [start number](@)
5240of an [ordered list] is determined by the list number of
5241its initial list item. The numbers of subsequent list items are
5242disregarded.
5243
5244A list is [loose](@) if any of its constituent
5245list items are separated by blank lines, or if any of its constituent
5246list items directly contain two block-level elements with a blank line
5247between them. Otherwise a list is [tight](@).
5248(The difference in HTML output is that paragraphs in a loose list are
5249wrapped in `<p>` tags, while paragraphs in a tight list are not.)
5250
5251Changing the bullet or ordered list delimiter starts a new list:
5252
5253```````````````````````````````` example
5254- foo
5255- bar
5256+ baz
5257.
5258<ul>
5259<li>foo</li>
5260<li>bar</li>
5261</ul>
5262<ul>
5263<li>baz</li>
5264</ul>
5265````````````````````````````````
5266
5267
5268```````````````````````````````` example
52691. foo
52702. bar
52713) baz
5272.
5273<ol>
5274<li>foo</li>
5275<li>bar</li>
5276</ol>
5277<ol start="3">
5278<li>baz</li>
5279</ol>
5280````````````````````````````````
5281
5282
5283In CommonMark, a list can interrupt a paragraph. That is,
5284no blank line is needed to separate a paragraph from a following
5285list:
5286
5287```````````````````````````````` example
5288Foo
5289- bar
5290- baz
5291.
5292<p>Foo</p>
5293<ul>
5294<li>bar</li>
5295<li>baz</li>
5296</ul>
5297````````````````````````````````
5298
5299`Markdown.pl` does not allow this, through fear of triggering a list
5300via a numeral in a hard-wrapped line:
5301
5302``` markdown
5303The number of windows in my house is
530414. The number of doors is 6.
5305```
5306
5307Oddly, though, `Markdown.pl` *does* allow a blockquote to
5308interrupt a paragraph, even though the same considerations might
5309apply.
5310
5311In CommonMark, we do allow lists to interrupt paragraphs, for
5312two reasons. First, it is natural and not uncommon for people
5313to start lists without blank lines:
5314
5315``` markdown
5316I need to buy
5317- new shoes
5318- a coat
5319- a plane ticket
5320```
5321
5322Second, we are attracted to a
5323
5324> [principle of uniformity](@):
5325> if a chunk of text has a certain
5326> meaning, it will continue to have the same meaning when put into a
5327> container block (such as a list item or blockquote).
5328
5329(Indeed, the spec for [list items] and [block quotes] presupposes
5330this principle.) This principle implies that if
5331
5332``` markdown
5333 * I need to buy
5334 - new shoes
5335 - a coat
5336 - a plane ticket
5337```
5338
5339is a list item containing a paragraph followed by a nested sublist,
5340as all Markdown implementations agree it is (though the paragraph
5341may be rendered without `<p>` tags, since the list is "tight"),
5342then
5343
5344``` markdown
5345I need to buy
5346- new shoes
5347- a coat
5348- a plane ticket
5349```
5350
5351by itself should be a paragraph followed by a nested sublist.
5352
5353Since it is well established Markdown practice to allow lists to
5354interrupt paragraphs inside list items, the [principle of
5355uniformity] requires us to allow this outside list items as
5356well. ([reStructuredText](http://docutils.sourceforge.net/rst.html)
5357takes a different approach, requiring blank lines before lists
5358even inside other list items.)
5359
5360In order to solve of unwanted lists in paragraphs with
5361hard-wrapped numerals, we allow only lists starting with `1` to
5362interrupt paragraphs. Thus,
5363
5364```````````````````````````````` example
5365The number of windows in my house is
536614. The number of doors is 6.
5367.
5368<p>The number of windows in my house is
536914. The number of doors is 6.</p>
5370````````````````````````````````
5371
5372We may still get an unintended result in cases like
5373
5374```````````````````````````````` example
5375The number of windows in my house is
53761. The number of doors is 6.
5377.
5378<p>The number of windows in my house is</p>
5379<ol>
5380<li>The number of doors is 6.</li>
5381</ol>
5382````````````````````````````````
5383
5384but this rule should prevent most spurious list captures.
5385
5386There can be any number of blank lines between items:
5387
5388```````````````````````````````` example
5389- foo
5390
5391- bar
5392
5393
5394- baz
5395.
5396<ul>
5397<li>
5398<p>foo</p>
5399</li>
5400<li>
5401<p>bar</p>
5402</li>
5403<li>
5404<p>baz</p>
5405</li>
5406</ul>
5407````````````````````````````````
5408
5409```````````````````````````````` example
5410- foo
5411 - bar
5412 - baz
5413
5414
5415 bim
5416.
5417<ul>
5418<li>foo
5419<ul>
5420<li>bar
5421<ul>
5422<li>
5423<p>baz</p>
5424<p>bim</p>
5425</li>
5426</ul>
5427</li>
5428</ul>
5429</li>
5430</ul>
5431````````````````````````````````
5432
5433
5434To separate consecutive lists of the same type, or to separate a
5435list from an indented code block that would otherwise be parsed
5436as a subparagraph of the final list item, you can insert a blank HTML
5437comment:
5438
5439```````````````````````````````` example
5440- foo
5441- bar
5442
5443<!-- -->
5444
5445- baz
5446- bim
5447.
5448<ul>
5449<li>foo</li>
5450<li>bar</li>
5451</ul>
5452<!-- -->
5453<ul>
5454<li>baz</li>
5455<li>bim</li>
5456</ul>
5457````````````````````````````````
5458
5459
5460```````````````````````````````` example
5461- foo
5462
5463 notcode
5464
5465- foo
5466
5467<!-- -->
5468
5469 code
5470.
5471<ul>
5472<li>
5473<p>foo</p>
5474<p>notcode</p>
5475</li>
5476<li>
5477<p>foo</p>
5478</li>
5479</ul>
5480<!-- -->
5481<pre><code>code
5482</code></pre>
5483````````````````````````````````
5484
5485
5486List items need not be indented to the same level. The following
5487list items will be treated as items at the same list level,
5488since none is indented enough to belong to the previous list
5489item:
5490
5491```````````````````````````````` example
5492- a
5493 - b
5494 - c
5495 - d
5496 - e
5497 - f
5498- g
5499.
5500<ul>
5501<li>a</li>
5502<li>b</li>
5503<li>c</li>
5504<li>d</li>
5505<li>e</li>
5506<li>f</li>
5507<li>g</li>
5508</ul>
5509````````````````````````````````
5510
5511
5512```````````````````````````````` example
55131. a
5514
5515 2. b
5516
5517 3. c
5518.
5519<ol>
5520<li>
5521<p>a</p>
5522</li>
5523<li>
5524<p>b</p>
5525</li>
5526<li>
5527<p>c</p>
5528</li>
5529</ol>
5530````````````````````````````````
5531
5532Note, however, that list items may not be preceded by more than
5533three spaces of indentation. Here `- e` is treated as a paragraph continuation
5534line, because it is indented more than three spaces:
5535
5536```````````````````````````````` example
5537- a
5538 - b
5539 - c
5540 - d
5541 - e
5542.
5543<ul>
5544<li>a</li>
5545<li>b</li>
5546<li>c</li>
5547<li>d
5548- e</li>
5549</ul>
5550````````````````````````````````
5551
5552And here, `3. c` is treated as in indented code block,
5553because it is indented four spaces and preceded by a
5554blank line.
5555
5556```````````````````````````````` example
55571. a
5558
5559 2. b
5560
5561 3. c
5562.
5563<ol>
5564<li>
5565<p>a</p>
5566</li>
5567<li>
5568<p>b</p>
5569</li>
5570</ol>
5571<pre><code>3. c
5572</code></pre>
5573````````````````````````````````
5574
5575
5576This is a loose list, because there is a blank line between
5577two of the list items:
5578
5579```````````````````````````````` example
5580- a
5581- b
5582
5583- c
5584.
5585<ul>
5586<li>
5587<p>a</p>
5588</li>
5589<li>
5590<p>b</p>
5591</li>
5592<li>
5593<p>c</p>
5594</li>
5595</ul>
5596````````````````````````````````
5597
5598
5599So is this, with a empty second item:
5600
5601```````````````````````````````` example
5602* a
5603*
5604
5605* c
5606.
5607<ul>
5608<li>
5609<p>a</p>
5610</li>
5611<li></li>
5612<li>
5613<p>c</p>
5614</li>
5615</ul>
5616````````````````````````````````
5617
5618
5619These are loose lists, even though there are no blank lines between the items,
5620because one of the items directly contains two block-level elements
5621with a blank line between them:
5622
5623```````````````````````````````` example
5624- a
5625- b
5626
5627 c
5628- d
5629.
5630<ul>
5631<li>
5632<p>a</p>
5633</li>
5634<li>
5635<p>b</p>
5636<p>c</p>
5637</li>
5638<li>
5639<p>d</p>
5640</li>
5641</ul>
5642````````````````````````````````
5643
5644
5645```````````````````````````````` example
5646- a
5647- b
5648
5649 [ref]: /url
5650- d
5651.
5652<ul>
5653<li>
5654<p>a</p>
5655</li>
5656<li>
5657<p>b</p>
5658</li>
5659<li>
5660<p>d</p>
5661</li>
5662</ul>
5663````````````````````````````````
5664
5665
5666This is a tight list, because the blank lines are in a code block:
5667
5668```````````````````````````````` example
5669- a
5670- ```
5671 b
5672
5673
5674 ```
5675- c
5676.
5677<ul>
5678<li>a</li>
5679<li>
5680<pre><code>b
5681
5682
5683</code></pre>
5684</li>
5685<li>c</li>
5686</ul>
5687````````````````````````````````
5688
5689
5690This is a tight list, because the blank line is between two
5691paragraphs of a sublist. So the sublist is loose while
5692the outer list is tight:
5693
5694```````````````````````````````` example
5695- a
5696 - b
5697
5698 c
5699- d
5700.
5701<ul>
5702<li>a
5703<ul>
5704<li>
5705<p>b</p>
5706<p>c</p>
5707</li>
5708</ul>
5709</li>
5710<li>d</li>
5711</ul>
5712````````````````````````````````
5713
5714
5715This is a tight list, because the blank line is inside the
5716block quote:
5717
5718```````````````````````````````` example
5719* a
5720 > b
5721 >
5722* c
5723.
5724<ul>
5725<li>a
5726<blockquote>
5727<p>b</p>
5728</blockquote>
5729</li>
5730<li>c</li>
5731</ul>
5732````````````````````````````````
5733
5734
5735This list is tight, because the consecutive block elements
5736are not separated by blank lines:
5737
5738```````````````````````````````` example
5739- a
5740 > b
5741 ```
5742 c
5743 ```
5744- d
5745.
5746<ul>
5747<li>a
5748<blockquote>
5749<p>b</p>
5750</blockquote>
5751<pre><code>c
5752</code></pre>
5753</li>
5754<li>d</li>
5755</ul>
5756````````````````````````````````
5757
5758
5759A single-paragraph list is tight:
5760
5761```````````````````````````````` example
5762- a
5763.
5764<ul>
5765<li>a</li>
5766</ul>
5767````````````````````````````````
5768
5769
5770```````````````````````````````` example
5771- a
5772 - b
5773.
5774<ul>
5775<li>a
5776<ul>
5777<li>b</li>
5778</ul>
5779</li>
5780</ul>
5781````````````````````````````````
5782
5783
5784This list is loose, because of the blank line between the
5785two block elements in the list item:
5786
5787```````````````````````````````` example
57881. ```
5789 foo
5790 ```
5791
5792 bar
5793.
5794<ol>
5795<li>
5796<pre><code>foo
5797</code></pre>
5798<p>bar</p>
5799</li>
5800</ol>
5801````````````````````````````````
5802
5803
5804Here the outer list is loose, the inner list tight:
5805
5806```````````````````````````````` example
5807* foo
5808 * bar
5809
5810 baz
5811.
5812<ul>
5813<li>
5814<p>foo</p>
5815<ul>
5816<li>bar</li>
5817</ul>
5818<p>baz</p>
5819</li>
5820</ul>
5821````````````````````````````````
5822
5823
5824```````````````````````````````` example
5825- a
5826 - b
5827 - c
5828
5829- d
5830 - e
5831 - f
5832.
5833<ul>
5834<li>
5835<p>a</p>
5836<ul>
5837<li>b</li>
5838<li>c</li>
5839</ul>
5840</li>
5841<li>
5842<p>d</p>
5843<ul>
5844<li>e</li>
5845<li>f</li>
5846</ul>
5847</li>
5848</ul>
5849````````````````````````````````
5850
5851
5852# Inlines
5853
5854Inlines are parsed sequentially from the beginning of the character
5855stream to the end (left to right, in left-to-right languages).
5856Thus, for example, in
5857
5858```````````````````````````````` example
5859`hi`lo`
5860.
5861<p><code>hi</code>lo`</p>
5862````````````````````````````````
5863
5864`hi` is parsed as code, leaving the backtick at the end as a literal
5865backtick.
5866
5867
5868
5869## Code spans
5870
5871A [backtick string](@)
5872is a string of one or more backtick characters (`` ` ``) that is neither
5873preceded nor followed by a backtick.
5874
5875A [code span](@) begins with a backtick string and ends with
5876a backtick string of equal length. The contents of the code span are
5877the characters between these two backtick strings, normalized in the
5878following ways:
5879
5880- First, [line endings] are converted to [spaces].
5881- If the resulting string both begins *and* ends with a [space]
5882 character, but does not consist entirely of [space]
5883 characters, a single [space] character is removed from the
5884 front and back. This allows you to include code that begins
5885 or ends with backtick characters, which must be separated by
5886 whitespace from the opening or closing backtick strings.
5887
5888This is a simple code span:
5889
5890```````````````````````````````` example
5891`foo`
5892.
5893<p><code>foo</code></p>
5894````````````````````````````````
5895
5896
5897Here two backticks are used, because the code contains a backtick.
5898This example also illustrates stripping of a single leading and
5899trailing space:
5900
5901```````````````````````````````` example
5902`` foo ` bar ``
5903.
5904<p><code>foo ` bar</code></p>
5905````````````````````````````````
5906
5907
5908This example shows the motivation for stripping leading and trailing
5909spaces:
5910
5911```````````````````````````````` example
5912` `` `
5913.
5914<p><code>``</code></p>
5915````````````````````````````````
5916
5917Note that only *one* space is stripped:
5918
5919```````````````````````````````` example
5920` `` `
5921.
5922<p><code> `` </code></p>
5923````````````````````````````````
5924
5925The stripping only happens if the space is on both
5926sides of the string:
5927
5928```````````````````````````````` example
5929` a`
5930.
5931<p><code> a</code></p>
5932````````````````````````````````
5933
5934Only [spaces], and not [unicode whitespace] in general, are
5935stripped in this way:
5936
5937```````````````````````````````` example
5938` b `
5939.
5940<p><code> b </code></p>
5941````````````````````````````````
5942
5943No stripping occurs if the code span contains only spaces:
5944
5945```````````````````````````````` example
5946` `
5947` `
5948.
5949<p><code> </code>
5950<code> </code></p>
5951````````````````````````````````
5952
5953
5954[Line endings] are treated like spaces:
5955
5956```````````````````````````````` example
5957``
5958foo
5959bar
5960baz
5961``
5962.
5963<p><code>foo bar baz</code></p>
5964````````````````````````````````
5965
5966```````````````````````````````` example
5967``
5968foo
5969``
5970.
5971<p><code>foo </code></p>
5972````````````````````````````````
5973
5974
5975Interior spaces are not collapsed:
5976
5977```````````````````````````````` example
5978`foo bar
5979baz`
5980.
5981<p><code>foo bar baz</code></p>
5982````````````````````````````````
5983
5984Note that browsers will typically collapse consecutive spaces
5985when rendering `<code>` elements, so it is recommended that
5986the following CSS be used:
5987
5988 code{white-space: pre-wrap;}
5989
5990
5991Note that backslash escapes do not work in code spans. All backslashes
5992are treated literally:
5993
5994```````````````````````````````` example
5995`foo\`bar`
5996.
5997<p><code>foo\</code>bar`</p>
5998````````````````````````````````
5999
6000
6001Backslash escapes are never needed, because one can always choose a
6002string of *n* backtick characters as delimiters, where the code does
6003not contain any strings of exactly *n* backtick characters.
6004
6005```````````````````````````````` example
6006``foo`bar``
6007.
6008<p><code>foo`bar</code></p>
6009````````````````````````````````
6010
6011```````````````````````````````` example
6012` foo `` bar `
6013.
6014<p><code>foo `` bar</code></p>
6015````````````````````````````````
6016
6017
6018Code span backticks have higher precedence than any other inline
6019constructs except HTML tags and autolinks. Thus, for example, this is
6020not parsed as emphasized text, since the second `*` is part of a code
6021span:
6022
6023```````````````````````````````` example
6024*foo`*`
6025.
6026<p>*foo<code>*</code></p>
6027````````````````````````````````
6028
6029
6030And this is not parsed as a link:
6031
6032```````````````````````````````` example
6033[not a `link](/foo`)
6034.
6035<p>[not a <code>link](/foo</code>)</p>
6036````````````````````````````````
6037
6038
6039Code spans, HTML tags, and autolinks have the same precedence.
6040Thus, this is code:
6041
6042```````````````````````````````` example
6043`<a href="`">`
6044.
6045<p><code>&lt;a href=&quot;</code>&quot;&gt;`</p>
6046````````````````````````````````
6047
6048
6049But this is an HTML tag:
6050
6051```````````````````````````````` example
6052<a href="`">`
6053.
6054<p><a href="`">`</p>
6055````````````````````````````````
6056
6057
6058And this is code:
6059
6060```````````````````````````````` example
6061`<http://foo.bar.`baz>`
6062.
6063<p><code>&lt;http://foo.bar.</code>baz&gt;`</p>
6064````````````````````````````````
6065
6066
6067But this is an autolink:
6068
6069```````````````````````````````` example
6070<http://foo.bar.`baz>`
6071.
6072<p><a href="http://foo.bar.%60baz">http://foo.bar.`baz</a>`</p>
6073````````````````````````````````
6074
6075
6076When a backtick string is not closed by a matching backtick string,
6077we just have literal backticks:
6078
6079```````````````````````````````` example
6080```foo``
6081.
6082<p>```foo``</p>
6083````````````````````````````````
6084
6085
6086```````````````````````````````` example
6087`foo
6088.
6089<p>`foo</p>
6090````````````````````````````````
6091
6092The following case also illustrates the need for opening and
6093closing backtick strings to be equal in length:
6094
6095```````````````````````````````` example
6096`foo``bar``
6097.
6098<p>`foo<code>bar</code></p>
6099````````````````````````````````
6100
6101
6102## Emphasis and strong emphasis
6103
6104John Gruber's original [Markdown syntax
6105description](http://daringfireball.net/projects/markdown/syntax#em) says:
6106
6107> Markdown treats asterisks (`*`) and underscores (`_`) as indicators of
6108> emphasis. Text wrapped with one `*` or `_` will be wrapped with an HTML
6109> `<em>` tag; double `*`'s or `_`'s will be wrapped with an HTML `<strong>`
6110> tag.
6111
6112This is enough for most users, but these rules leave much undecided,
6113especially when it comes to nested emphasis. The original
6114`Markdown.pl` test suite makes it clear that triple `***` and
6115`___` delimiters can be used for strong emphasis, and most
6116implementations have also allowed the following patterns:
6117
6118``` markdown
6119***strong emph***
6120***strong** in emph*
6121***emph* in strong**
6122**in strong *emph***
6123*in emph **strong***
6124```
6125
6126The following patterns are less widely supported, but the intent
6127is clear and they are useful (especially in contexts like bibliography
6128entries):
6129
6130``` markdown
6131*emph *with emph* in it*
6132**strong **with strong** in it**
6133```
6134
6135Many implementations have also restricted intraword emphasis to
6136the `*` forms, to avoid unwanted emphasis in words containing
6137internal underscores. (It is best practice to put these in code
6138spans, but users often do not.)
6139
6140``` markdown
6141internal emphasis: foo*bar*baz
6142no emphasis: foo_bar_baz
6143```
6144
6145The rules given below capture all of these patterns, while allowing
6146for efficient parsing strategies that do not backtrack.
6147
6148First, some definitions. A [delimiter run](@) is either
6149a sequence of one or more `*` characters that is not preceded or
6150followed by a non-backslash-escaped `*` character, or a sequence
6151of one or more `_` characters that is not preceded or followed by
6152a non-backslash-escaped `_` character.
6153
6154A [left-flanking delimiter run](@) is
6155a [delimiter run] that is (1) not followed by [Unicode whitespace],
6156and either (2a) not followed by a [Unicode punctuation character], or
6157(2b) followed by a [Unicode punctuation character] and
6158preceded by [Unicode whitespace] or a [Unicode punctuation character].
6159For purposes of this definition, the beginning and the end of
6160the line count as Unicode whitespace.
6161
6162A [right-flanking delimiter run](@) is
6163a [delimiter run] that is (1) not preceded by [Unicode whitespace],
6164and either (2a) not preceded by a [Unicode punctuation character], or
6165(2b) preceded by a [Unicode punctuation character] and
6166followed by [Unicode whitespace] or a [Unicode punctuation character].
6167For purposes of this definition, the beginning and the end of
6168the line count as Unicode whitespace.
6169
6170Here are some examples of delimiter runs.
6171
6172 - left-flanking but not right-flanking:
6173
6174 ```
6175 ***abc
6176 _abc
6177 **"abc"
6178 _"abc"
6179 ```
6180
6181 - right-flanking but not left-flanking:
6182
6183 ```
6184 abc***
6185 abc_
6186 "abc"**
6187 "abc"_
6188 ```
6189
6190 - Both left and right-flanking:
6191
6192 ```
6193 abc***def
6194 "abc"_"def"
6195 ```
6196
6197 - Neither left nor right-flanking:
6198
6199 ```
6200 abc *** def
6201 a _ b
6202 ```
6203
6204(The idea of distinguishing left-flanking and right-flanking
6205delimiter runs based on the character before and the character
6206after comes from Roopesh Chander's
6207[vfmd](http://www.vfmd.org/vfmd-spec/specification/#procedure-for-identifying-emphasis-tags).
6208vfmd uses the terminology "emphasis indicator string" instead of "delimiter
6209run," and its rules for distinguishing left- and right-flanking runs
6210are a bit more complex than the ones given here.)
6211
6212The following rules define emphasis and strong emphasis:
6213
62141. A single `*` character [can open emphasis](@)
6215 iff (if and only if) it is part of a [left-flanking delimiter run].
6216
62172. A single `_` character [can open emphasis] iff
6218 it is part of a [left-flanking delimiter run]
6219 and either (a) not part of a [right-flanking delimiter run]
6220 or (b) part of a [right-flanking delimiter run]
6221 preceded by a [Unicode punctuation character].
6222
62233. A single `*` character [can close emphasis](@)
6224 iff it is part of a [right-flanking delimiter run].
6225
62264. A single `_` character [can close emphasis] iff
6227 it is part of a [right-flanking delimiter run]
6228 and either (a) not part of a [left-flanking delimiter run]
6229 or (b) part of a [left-flanking delimiter run]
6230 followed by a [Unicode punctuation character].
6231
62325. A double `**` [can open strong emphasis](@)
6233 iff it is part of a [left-flanking delimiter run].
6234
62356. A double `__` [can open strong emphasis] iff
6236 it is part of a [left-flanking delimiter run]
6237 and either (a) not part of a [right-flanking delimiter run]
6238 or (b) part of a [right-flanking delimiter run]
6239 preceded by a [Unicode punctuation character].
6240
62417. A double `**` [can close strong emphasis](@)
6242 iff it is part of a [right-flanking delimiter run].
6243
62448. A double `__` [can close strong emphasis] iff
6245 it is part of a [right-flanking delimiter run]
6246 and either (a) not part of a [left-flanking delimiter run]
6247 or (b) part of a [left-flanking delimiter run]
6248 followed by a [Unicode punctuation character].
6249
62509. Emphasis begins with a delimiter that [can open emphasis] and ends
6251 with a delimiter that [can close emphasis], and that uses the same
6252 character (`_` or `*`) as the opening delimiter. The
6253 opening and closing delimiters must belong to separate
6254 [delimiter runs]. If one of the delimiters can both
6255 open and close emphasis, then the sum of the lengths of the
6256 delimiter runs containing the opening and closing delimiters
6257 must not be a multiple of 3 unless both lengths are
6258 multiples of 3.
6259
626010. Strong emphasis begins with a delimiter that
6261 [can open strong emphasis] and ends with a delimiter that
6262 [can close strong emphasis], and that uses the same character
6263 (`_` or `*`) as the opening delimiter. The
6264 opening and closing delimiters must belong to separate
6265 [delimiter runs]. If one of the delimiters can both open
6266 and close strong emphasis, then the sum of the lengths of
6267 the delimiter runs containing the opening and closing
6268 delimiters must not be a multiple of 3 unless both lengths
6269 are multiples of 3.
6270
627111. A literal `*` character cannot occur at the beginning or end of
6272 `*`-delimited emphasis or `**`-delimited strong emphasis, unless it
6273 is backslash-escaped.
6274
627512. A literal `_` character cannot occur at the beginning or end of
6276 `_`-delimited emphasis or `__`-delimited strong emphasis, unless it
6277 is backslash-escaped.
6278
6279Where rules 1--12 above are compatible with multiple parsings,
6280the following principles resolve ambiguity:
6281
628213. The number of nestings should be minimized. Thus, for example,
6283 an interpretation `<strong>...</strong>` is always preferred to
6284 `<em><em>...</em></em>`.
6285
628614. An interpretation `<em><strong>...</strong></em>` is always
6287 preferred to `<strong><em>...</em></strong>`.
6288
628915. When two potential emphasis or strong emphasis spans overlap,
6290 so that the second begins before the first ends and ends after
6291 the first ends, the first takes precedence. Thus, for example,
6292 `*foo _bar* baz_` is parsed as `<em>foo _bar</em> baz_` rather
6293 than `*foo <em>bar* baz</em>`.
6294
629516. When there are two potential emphasis or strong emphasis spans
6296 with the same closing delimiter, the shorter one (the one that
6297 opens later) takes precedence. Thus, for example,
6298 `**foo **bar baz**` is parsed as `**foo <strong>bar baz</strong>`
6299 rather than `<strong>foo **bar baz</strong>`.
6300
630117. Inline code spans, links, images, and HTML tags group more tightly
6302 than emphasis. So, when there is a choice between an interpretation
6303 that contains one of these elements and one that does not, the
6304 former always wins. Thus, for example, `*[foo*](bar)` is
6305 parsed as `*<a href="bar">foo*</a>` rather than as
6306 `<em>[foo</em>](bar)`.
6307
6308These rules can be illustrated through a series of examples.
6309
6310Rule 1:
6311
6312```````````````````````````````` example
6313*foo bar*
6314.
6315<p><em>foo bar</em></p>
6316````````````````````````````````
6317
6318
6319This is not emphasis, because the opening `*` is followed by
6320whitespace, and hence not part of a [left-flanking delimiter run]:
6321
6322```````````````````````````````` example
6323a * foo bar*
6324.
6325<p>a * foo bar*</p>
6326````````````````````````````````
6327
6328
6329This is not emphasis, because the opening `*` is preceded
6330by an alphanumeric and followed by punctuation, and hence
6331not part of a [left-flanking delimiter run]:
6332
6333```````````````````````````````` example
6334a*"foo"*
6335.
6336<p>a*&quot;foo&quot;*</p>
6337````````````````````````````````
6338
6339
6340Unicode nonbreaking spaces count as whitespace, too:
6341
6342```````````````````````````````` example
6343* a *
6344.
6345<p>* a *</p>
6346````````````````````````````````
6347
6348
6349Intraword emphasis with `*` is permitted:
6350
6351```````````````````````````````` example
6352foo*bar*
6353.
6354<p>foo<em>bar</em></p>
6355````````````````````````````````
6356
6357
6358```````````````````````````````` example
63595*6*78
6360.
6361<p>5<em>6</em>78</p>
6362````````````````````````````````
6363
6364
6365Rule 2:
6366
6367```````````````````````````````` example
6368_foo bar_
6369.
6370<p><em>foo bar</em></p>
6371````````````````````````````````
6372
6373
6374This is not emphasis, because the opening `_` is followed by
6375whitespace:
6376
6377```````````````````````````````` example
6378_ foo bar_
6379.
6380<p>_ foo bar_</p>
6381````````````````````````````````
6382
6383
6384This is not emphasis, because the opening `_` is preceded
6385by an alphanumeric and followed by punctuation:
6386
6387```````````````````````````````` example
6388a_"foo"_
6389.
6390<p>a_&quot;foo&quot;_</p>
6391````````````````````````````````
6392
6393
6394Emphasis with `_` is not allowed inside words:
6395
6396```````````````````````````````` example
6397foo_bar_
6398.
6399<p>foo_bar_</p>
6400````````````````````````````````
6401
6402
6403```````````````````````````````` example
64045_6_78
6405.
6406<p>5_6_78</p>
6407````````````````````````````````
6408
6409
6410```````````````````````````````` example
6411пристаням_стремятся_
6412.
6413<p>пристаням_стремятся_</p>
6414````````````````````````````````
6415
6416
6417Here `_` does not generate emphasis, because the first delimiter run
6418is right-flanking and the second left-flanking:
6419
6420```````````````````````````````` example
6421aa_"bb"_cc
6422.
6423<p>aa_&quot;bb&quot;_cc</p>
6424````````````````````````````````
6425
6426
6427This is emphasis, even though the opening delimiter is
6428both left- and right-flanking, because it is preceded by
6429punctuation:
6430
6431```````````````````````````````` example
6432foo-_(bar)_
6433.
6434<p>foo-<em>(bar)</em></p>
6435````````````````````````````````
6436
6437
6438Rule 3:
6439
6440This is not emphasis, because the closing delimiter does
6441not match the opening delimiter:
6442
6443```````````````````````````````` example
6444_foo*
6445.
6446<p>_foo*</p>
6447````````````````````````````````
6448
6449
6450This is not emphasis, because the closing `*` is preceded by
6451whitespace:
6452
6453```````````````````````````````` example
6454*foo bar *
6455.
6456<p>*foo bar *</p>
6457````````````````````````````````
6458
6459
6460A line ending also counts as whitespace:
6461
6462```````````````````````````````` example
6463*foo bar
6464*
6465.
6466<p>*foo bar
6467*</p>
6468````````````````````````````````
6469
6470
6471This is not emphasis, because the second `*` is
6472preceded by punctuation and followed by an alphanumeric
6473(hence it is not part of a [right-flanking delimiter run]:
6474
6475```````````````````````````````` example
6476*(*foo)
6477.
6478<p>*(*foo)</p>
6479````````````````````````````````
6480
6481
6482The point of this restriction is more easily appreciated
6483with this example:
6484
6485```````````````````````````````` example
6486*(*foo*)*
6487.
6488<p><em>(<em>foo</em>)</em></p>
6489````````````````````````````````
6490
6491
6492Intraword emphasis with `*` is allowed:
6493
6494```````````````````````````````` example
6495*foo*bar
6496.
6497<p><em>foo</em>bar</p>
6498````````````````````````````````
6499
6500
6501
6502Rule 4:
6503
6504This is not emphasis, because the closing `_` is preceded by
6505whitespace:
6506
6507```````````````````````````````` example
6508_foo bar _
6509.
6510<p>_foo bar _</p>
6511````````````````````````````````
6512
6513
6514This is not emphasis, because the second `_` is
6515preceded by punctuation and followed by an alphanumeric:
6516
6517```````````````````````````````` example
6518_(_foo)
6519.
6520<p>_(_foo)</p>
6521````````````````````````````````
6522
6523
6524This is emphasis within emphasis:
6525
6526```````````````````````````````` example
6527_(_foo_)_
6528.
6529<p><em>(<em>foo</em>)</em></p>
6530````````````````````````````````
6531
6532
6533Intraword emphasis is disallowed for `_`:
6534
6535```````````````````````````````` example
6536_foo_bar
6537.
6538<p>_foo_bar</p>
6539````````````````````````````````
6540
6541
6542```````````````````````````````` example
6543_пристаням_стремятся
6544.
6545<p>_пристаням_стремятся</p>
6546````````````````````````````````
6547
6548
6549```````````````````````````````` example
6550_foo_bar_baz_
6551.
6552<p><em>foo_bar_baz</em></p>
6553````````````````````````````````
6554
6555
6556This is emphasis, even though the closing delimiter is
6557both left- and right-flanking, because it is followed by
6558punctuation:
6559
6560```````````````````````````````` example
6561_(bar)_.
6562.
6563<p><em>(bar)</em>.</p>
6564````````````````````````````````
6565
6566
6567Rule 5:
6568
6569```````````````````````````````` example
6570**foo bar**
6571.
6572<p><strong>foo bar</strong></p>
6573````````````````````````````````
6574
6575
6576This is not strong emphasis, because the opening delimiter is
6577followed by whitespace:
6578
6579```````````````````````````````` example
6580** foo bar**
6581.
6582<p>** foo bar**</p>
6583````````````````````````````````
6584
6585
6586This is not strong emphasis, because the opening `**` is preceded
6587by an alphanumeric and followed by punctuation, and hence
6588not part of a [left-flanking delimiter run]:
6589
6590```````````````````````````````` example
6591a**"foo"**
6592.
6593<p>a**&quot;foo&quot;**</p>
6594````````````````````````````````
6595
6596
6597Intraword strong emphasis with `**` is permitted:
6598
6599```````````````````````````````` example
6600foo**bar**
6601.
6602<p>foo<strong>bar</strong></p>
6603````````````````````````````````
6604
6605
6606Rule 6:
6607
6608```````````````````````````````` example
6609__foo bar__
6610.
6611<p><strong>foo bar</strong></p>
6612````````````````````````````````
6613
6614
6615This is not strong emphasis, because the opening delimiter is
6616followed by whitespace:
6617
6618```````````````````````````````` example
6619__ foo bar__
6620.
6621<p>__ foo bar__</p>
6622````````````````````````````````
6623
6624
6625A line ending counts as whitespace:
6626```````````````````````````````` example
6627__
6628foo bar__
6629.
6630<p>__
6631foo bar__</p>
6632````````````````````````````````
6633
6634
6635This is not strong emphasis, because the opening `__` is preceded
6636by an alphanumeric and followed by punctuation:
6637
6638```````````````````````````````` example
6639a__"foo"__
6640.
6641<p>a__&quot;foo&quot;__</p>
6642````````````````````````````````
6643
6644
6645Intraword strong emphasis is forbidden with `__`:
6646
6647```````````````````````````````` example
6648foo__bar__
6649.
6650<p>foo__bar__</p>
6651````````````````````````````````
6652
6653
6654```````````````````````````````` example
66555__6__78
6656.
6657<p>5__6__78</p>
6658````````````````````````````````
6659
6660
6661```````````````````````````````` example
6662пристаням__стремятся__
6663.
6664<p>пристаням__стремятся__</p>
6665````````````````````````````````
6666
6667
6668```````````````````````````````` example
6669__foo, __bar__, baz__
6670.
6671<p><strong>foo, <strong>bar</strong>, baz</strong></p>
6672````````````````````````````````
6673
6674
6675This is strong emphasis, even though the opening delimiter is
6676both left- and right-flanking, because it is preceded by
6677punctuation:
6678
6679```````````````````````````````` example
6680foo-__(bar)__
6681.
6682<p>foo-<strong>(bar)</strong></p>
6683````````````````````````````````
6684
6685
6686
6687Rule 7:
6688
6689This is not strong emphasis, because the closing delimiter is preceded
6690by whitespace:
6691
6692```````````````````````````````` example
6693**foo bar **
6694.
6695<p>**foo bar **</p>
6696````````````````````````````````
6697
6698
6699(Nor can it be interpreted as an emphasized `*foo bar *`, because of
6700Rule 11.)
6701
6702This is not strong emphasis, because the second `**` is
6703preceded by punctuation and followed by an alphanumeric:
6704
6705```````````````````````````````` example
6706**(**foo)
6707.
6708<p>**(**foo)</p>
6709````````````````````````````````
6710
6711
6712The point of this restriction is more easily appreciated
6713with these examples:
6714
6715```````````````````````````````` example
6716*(**foo**)*
6717.
6718<p><em>(<strong>foo</strong>)</em></p>
6719````````````````````````````````
6720
6721
6722```````````````````````````````` example
6723**Gomphocarpus (*Gomphocarpus physocarpus*, syn.
6724*Asclepias physocarpa*)**
6725.
6726<p><strong>Gomphocarpus (<em>Gomphocarpus physocarpus</em>, syn.
6727<em>Asclepias physocarpa</em>)</strong></p>
6728````````````````````````````````
6729
6730
6731```````````````````````````````` example
6732**foo "*bar*" foo**
6733.
6734<p><strong>foo &quot;<em>bar</em>&quot; foo</strong></p>
6735````````````````````````````````
6736
6737
6738Intraword emphasis:
6739
6740```````````````````````````````` example
6741**foo**bar
6742.
6743<p><strong>foo</strong>bar</p>
6744````````````````````````````````
6745
6746
6747Rule 8:
6748
6749This is not strong emphasis, because the closing delimiter is
6750preceded by whitespace:
6751
6752```````````````````````````````` example
6753__foo bar __
6754.
6755<p>__foo bar __</p>
6756````````````````````````````````
6757
6758
6759This is not strong emphasis, because the second `__` is
6760preceded by punctuation and followed by an alphanumeric:
6761
6762```````````````````````````````` example
6763__(__foo)
6764.
6765<p>__(__foo)</p>
6766````````````````````````````````
6767
6768
6769The point of this restriction is more easily appreciated
6770with this example:
6771
6772```````````````````````````````` example
6773_(__foo__)_
6774.
6775<p><em>(<strong>foo</strong>)</em></p>
6776````````````````````````````````
6777
6778
6779Intraword strong emphasis is forbidden with `__`:
6780
6781```````````````````````````````` example
6782__foo__bar
6783.
6784<p>__foo__bar</p>
6785````````````````````````````````
6786
6787
6788```````````````````````````````` example
6789__пристаням__стремятся
6790.
6791<p>__пристаням__стремятся</p>
6792````````````````````````````````
6793
6794
6795```````````````````````````````` example
6796__foo__bar__baz__
6797.
6798<p><strong>foo__bar__baz</strong></p>
6799````````````````````````````````
6800
6801
6802This is strong emphasis, even though the closing delimiter is
6803both left- and right-flanking, because it is followed by
6804punctuation:
6805
6806```````````````````````````````` example
6807__(bar)__.
6808.
6809<p><strong>(bar)</strong>.</p>
6810````````````````````````````````
6811
6812
6813Rule 9:
6814
6815Any nonempty sequence of inline elements can be the contents of an
6816emphasized span.
6817
6818```````````````````````````````` example
6819*foo [bar](/url)*
6820.
6821<p><em>foo <a href="/url">bar</a></em></p>
6822````````````````````````````````
6823
6824
6825```````````````````````````````` example
6826*foo
6827bar*
6828.
6829<p><em>foo
6830bar</em></p>
6831````````````````````````````````
6832
6833
6834In particular, emphasis and strong emphasis can be nested
6835inside emphasis:
6836
6837```````````````````````````````` example
6838_foo __bar__ baz_
6839.
6840<p><em>foo <strong>bar</strong> baz</em></p>
6841````````````````````````````````
6842
6843
6844```````````````````````````````` example
6845_foo _bar_ baz_
6846.
6847<p><em>foo <em>bar</em> baz</em></p>
6848````````````````````````````````
6849
6850
6851```````````````````````````````` example
6852__foo_ bar_
6853.
6854<p><em><em>foo</em> bar</em></p>
6855````````````````````````````````
6856
6857
6858```````````````````````````````` example
6859*foo *bar**
6860.
6861<p><em>foo <em>bar</em></em></p>
6862````````````````````````````````
6863
6864
6865```````````````````````````````` example
6866*foo **bar** baz*
6867.
6868<p><em>foo <strong>bar</strong> baz</em></p>
6869````````````````````````````````
6870
6871```````````````````````````````` example
6872*foo**bar**baz*
6873.
6874<p><em>foo<strong>bar</strong>baz</em></p>
6875````````````````````````````````
6876
6877Note that in the preceding case, the interpretation
6878
6879``` markdown
6880<p><em>foo</em><em>bar<em></em>baz</em></p>
6881```
6882
6883
6884is precluded by the condition that a delimiter that
6885can both open and close (like the `*` after `foo`)
6886cannot form emphasis if the sum of the lengths of
6887the delimiter runs containing the opening and
6888closing delimiters is a multiple of 3 unless
6889both lengths are multiples of 3.
6890
6891
6892For the same reason, we don't get two consecutive
6893emphasis sections in this example:
6894
6895```````````````````````````````` example
6896*foo**bar*
6897.
6898<p><em>foo**bar</em></p>
6899````````````````````````````````
6900
6901
6902The same condition ensures that the following
6903cases are all strong emphasis nested inside
6904emphasis, even when the interior whitespace is
6905omitted:
6906
6907
6908```````````````````````````````` example
6909***foo** bar*
6910.
6911<p><em><strong>foo</strong> bar</em></p>
6912````````````````````````````````
6913
6914
6915```````````````````````````````` example
6916*foo **bar***
6917.
6918<p><em>foo <strong>bar</strong></em></p>
6919````````````````````````````````
6920
6921
6922```````````````````````````````` example
6923*foo**bar***
6924.
6925<p><em>foo<strong>bar</strong></em></p>
6926````````````````````````````````
6927
6928
6929When the lengths of the interior closing and opening
6930delimiter runs are *both* multiples of 3, though,
6931they can match to create emphasis:
6932
6933```````````````````````````````` example
6934foo***bar***baz
6935.
6936<p>foo<em><strong>bar</strong></em>baz</p>
6937````````````````````````````````
6938
6939```````````````````````````````` example
6940foo******bar*********baz
6941.
6942<p>foo<strong><strong><strong>bar</strong></strong></strong>***baz</p>
6943````````````````````````````````
6944
6945
6946Indefinite levels of nesting are possible:
6947
6948```````````````````````````````` example
6949*foo **bar *baz* bim** bop*
6950.
6951<p><em>foo <strong>bar <em>baz</em> bim</strong> bop</em></p>
6952````````````````````````````````
6953
6954
6955```````````````````````````````` example
6956*foo [*bar*](/url)*
6957.
6958<p><em>foo <a href="/url"><em>bar</em></a></em></p>
6959````````````````````````````````
6960
6961
6962There can be no empty emphasis or strong emphasis:
6963
6964```````````````````````````````` example
6965** is not an empty emphasis
6966.
6967<p>** is not an empty emphasis</p>
6968````````````````````````````````
6969
6970
6971```````````````````````````````` example
6972**** is not an empty strong emphasis
6973.
6974<p>**** is not an empty strong emphasis</p>
6975````````````````````````````````
6976
6977
6978
6979Rule 10:
6980
6981Any nonempty sequence of inline elements can be the contents of an
6982strongly emphasized span.
6983
6984```````````````````````````````` example
6985**foo [bar](/url)**
6986.
6987<p><strong>foo <a href="/url">bar</a></strong></p>
6988````````````````````````````````
6989
6990
6991```````````````````````````````` example
6992**foo
6993bar**
6994.
6995<p><strong>foo
6996bar</strong></p>
6997````````````````````````````````
6998
6999
7000In particular, emphasis and strong emphasis can be nested
7001inside strong emphasis:
7002
7003```````````````````````````````` example
7004__foo _bar_ baz__
7005.
7006<p><strong>foo <em>bar</em> baz</strong></p>
7007````````````````````````````````
7008
7009
7010```````````````````````````````` example
7011__foo __bar__ baz__
7012.
7013<p><strong>foo <strong>bar</strong> baz</strong></p>
7014````````````````````````````````
7015
7016
7017```````````````````````````````` example
7018____foo__ bar__
7019.
7020<p><strong><strong>foo</strong> bar</strong></p>
7021````````````````````````````````
7022
7023
7024```````````````````````````````` example
7025**foo **bar****
7026.
7027<p><strong>foo <strong>bar</strong></strong></p>
7028````````````````````````````````
7029
7030
7031```````````````````````````````` example
7032**foo *bar* baz**
7033.
7034<p><strong>foo <em>bar</em> baz</strong></p>
7035````````````````````````````````
7036
7037
7038```````````````````````````````` example
7039**foo*bar*baz**
7040.
7041<p><strong>foo<em>bar</em>baz</strong></p>
7042````````````````````````````````
7043
7044
7045```````````````````````````````` example
7046***foo* bar**
7047.
7048<p><strong><em>foo</em> bar</strong></p>
7049````````````````````````````````
7050
7051
7052```````````````````````````````` example
7053**foo *bar***
7054.
7055<p><strong>foo <em>bar</em></strong></p>
7056````````````````````````````````
7057
7058
7059Indefinite levels of nesting are possible:
7060
7061```````````````````````````````` example
7062**foo *bar **baz**
7063bim* bop**
7064.
7065<p><strong>foo <em>bar <strong>baz</strong>
7066bim</em> bop</strong></p>
7067````````````````````````````````
7068
7069
7070```````````````````````````````` example
7071**foo [*bar*](/url)**
7072.
7073<p><strong>foo <a href="/url"><em>bar</em></a></strong></p>
7074````````````````````````````````
7075
7076
7077There can be no empty emphasis or strong emphasis:
7078
7079```````````````````````````````` example
7080__ is not an empty emphasis
7081.
7082<p>__ is not an empty emphasis</p>
7083````````````````````````````````
7084
7085
7086```````````````````````````````` example
7087____ is not an empty strong emphasis
7088.
7089<p>____ is not an empty strong emphasis</p>
7090````````````````````````````````
7091
7092
7093
7094Rule 11:
7095
7096```````````````````````````````` example
7097foo ***
7098.
7099<p>foo ***</p>
7100````````````````````````````````
7101
7102
7103```````````````````````````````` example
7104foo *\**
7105.
7106<p>foo <em>*</em></p>
7107````````````````````````````````
7108
7109
7110```````````````````````````````` example
7111foo *_*
7112.
7113<p>foo <em>_</em></p>
7114````````````````````````````````
7115
7116
7117```````````````````````````````` example
7118foo *****
7119.
7120<p>foo *****</p>
7121````````````````````````````````
7122
7123
7124```````````````````````````````` example
7125foo **\***
7126.
7127<p>foo <strong>*</strong></p>
7128````````````````````````````````
7129
7130
7131```````````````````````````````` example
7132foo **_**
7133.
7134<p>foo <strong>_</strong></p>
7135````````````````````````````````
7136
7137
7138Note that when delimiters do not match evenly, Rule 11 determines
7139that the excess literal `*` characters will appear outside of the
7140emphasis, rather than inside it:
7141
7142```````````````````````````````` example
7143**foo*
7144.
7145<p>*<em>foo</em></p>
7146````````````````````````````````
7147
7148
7149```````````````````````````````` example
7150*foo**
7151.
7152<p><em>foo</em>*</p>
7153````````````````````````````````
7154
7155
7156```````````````````````````````` example
7157***foo**
7158.
7159<p>*<strong>foo</strong></p>
7160````````````````````````````````
7161
7162
7163```````````````````````````````` example
7164****foo*
7165.
7166<p>***<em>foo</em></p>
7167````````````````````````````````
7168
7169
7170```````````````````````````````` example
7171**foo***
7172.
7173<p><strong>foo</strong>*</p>
7174````````````````````````````````
7175
7176
7177```````````````````````````````` example
7178*foo****
7179.
7180<p><em>foo</em>***</p>
7181````````````````````````````````
7182
7183
7184
7185Rule 12:
7186
7187```````````````````````````````` example
7188foo ___
7189.
7190<p>foo ___</p>
7191````````````````````````````````
7192
7193
7194```````````````````````````````` example
7195foo _\__
7196.
7197<p>foo <em>_</em></p>
7198````````````````````````````````
7199
7200
7201```````````````````````````````` example
7202foo _*_
7203.
7204<p>foo <em>*</em></p>
7205````````````````````````````````
7206
7207
7208```````````````````````````````` example
7209foo _____
7210.
7211<p>foo _____</p>
7212````````````````````````````````
7213
7214
7215```````````````````````````````` example
7216foo __\___
7217.
7218<p>foo <strong>_</strong></p>
7219````````````````````````````````
7220
7221
7222```````````````````````````````` example
7223foo __*__
7224.
7225<p>foo <strong>*</strong></p>
7226````````````````````````````````
7227
7228
7229```````````````````````````````` example
7230__foo_
7231.
7232<p>_<em>foo</em></p>
7233````````````````````````````````
7234
7235
7236Note that when delimiters do not match evenly, Rule 12 determines
7237that the excess literal `_` characters will appear outside of the
7238emphasis, rather than inside it:
7239
7240```````````````````````````````` example
7241_foo__
7242.
7243<p><em>foo</em>_</p>
7244````````````````````````````````
7245
7246
7247```````````````````````````````` example
7248___foo__
7249.
7250<p>_<strong>foo</strong></p>
7251````````````````````````````````
7252
7253
7254```````````````````````````````` example
7255____foo_
7256.
7257<p>___<em>foo</em></p>
7258````````````````````````````````
7259
7260
7261```````````````````````````````` example
7262__foo___
7263.
7264<p><strong>foo</strong>_</p>
7265````````````````````````````````
7266
7267
7268```````````````````````````````` example
7269_foo____
7270.
7271<p><em>foo</em>___</p>
7272````````````````````````````````
7273
7274
7275Rule 13 implies that if you want emphasis nested directly inside
7276emphasis, you must use different delimiters:
7277
7278```````````````````````````````` example
7279**foo**
7280.
7281<p><strong>foo</strong></p>
7282````````````````````````````````
7283
7284
7285```````````````````````````````` example
7286*_foo_*
7287.
7288<p><em><em>foo</em></em></p>
7289````````````````````````````````
7290
7291
7292```````````````````````````````` example
7293__foo__
7294.
7295<p><strong>foo</strong></p>
7296````````````````````````````````
7297
7298
7299```````````````````````````````` example
7300_*foo*_
7301.
7302<p><em><em>foo</em></em></p>
7303````````````````````````````````
7304
7305
7306However, strong emphasis within strong emphasis is possible without
7307switching delimiters:
7308
7309```````````````````````````````` example
7310****foo****
7311.
7312<p><strong><strong>foo</strong></strong></p>
7313````````````````````````````````
7314
7315
7316```````````````````````````````` example
7317____foo____
7318.
7319<p><strong><strong>foo</strong></strong></p>
7320````````````````````````````````
7321
7322
7323
7324Rule 13 can be applied to arbitrarily long sequences of
7325delimiters:
7326
7327```````````````````````````````` example
7328******foo******
7329.
7330<p><strong><strong><strong>foo</strong></strong></strong></p>
7331````````````````````````````````
7332
7333
7334Rule 14:
7335
7336```````````````````````````````` example
7337***foo***
7338.
7339<p><em><strong>foo</strong></em></p>
7340````````````````````````````````
7341
7342
7343```````````````````````````````` example
7344_____foo_____
7345.
7346<p><em><strong><strong>foo</strong></strong></em></p>
7347````````````````````````````````
7348
7349
7350Rule 15:
7351
7352```````````````````````````````` example
7353*foo _bar* baz_
7354.
7355<p><em>foo _bar</em> baz_</p>
7356````````````````````````````````
7357
7358
7359```````````````````````````````` example
7360*foo __bar *baz bim__ bam*
7361.
7362<p><em>foo <strong>bar *baz bim</strong> bam</em></p>
7363````````````````````````````````
7364
7365
7366Rule 16:
7367
7368```````````````````````````````` example
7369**foo **bar baz**
7370.
7371<p>**foo <strong>bar baz</strong></p>
7372````````````````````````````````
7373
7374
7375```````````````````````````````` example
7376*foo *bar baz*
7377.
7378<p>*foo <em>bar baz</em></p>
7379````````````````````````````````
7380
7381
7382Rule 17:
7383
7384```````````````````````````````` example
7385*[bar*](/url)
7386.
7387<p>*<a href="/url">bar*</a></p>
7388````````````````````````````````
7389
7390
7391```````````````````````````````` example
7392_foo [bar_](/url)
7393.
7394<p>_foo <a href="/url">bar_</a></p>
7395````````````````````````````````
7396
7397
7398```````````````````````````````` example
7399*<img src="foo" title="*"/>
7400.
7401<p>*<img src="foo" title="*"/></p>
7402````````````````````````````````
7403
7404
7405```````````````````````````````` example
7406**<a href="**">
7407.
7408<p>**<a href="**"></p>
7409````````````````````````````````
7410
7411
7412```````````````````````````````` example
7413__<a href="__">
7414.
7415<p>__<a href="__"></p>
7416````````````````````````````````
7417
7418
7419```````````````````````````````` example
7420*a `*`*
7421.
7422<p><em>a <code>*</code></em></p>
7423````````````````````````````````
7424
7425
7426```````````````````````````````` example
7427_a `_`_
7428.
7429<p><em>a <code>_</code></em></p>
7430````````````````````````````````
7431
7432
7433```````````````````````````````` example
7434**a<http://foo.bar/?q=**>
7435.
7436<p>**a<a href="http://foo.bar/?q=**">http://foo.bar/?q=**</a></p>
7437````````````````````````````````
7438
7439
7440```````````````````````````````` example
7441__a<http://foo.bar/?q=__>
7442.
7443<p>__a<a href="http://foo.bar/?q=__">http://foo.bar/?q=__</a></p>
7444````````````````````````````````
7445
7446
7447
7448## Links
7449
7450A link contains [link text] (the visible text), a [link destination]
7451(the URI that is the link destination), and optionally a [link title].
7452There are two basic kinds of links in Markdown. In [inline links] the
7453destination and title are given immediately after the link text. In
7454[reference links] the destination and title are defined elsewhere in
7455the document.
7456
7457A [link text](@) consists of a sequence of zero or more
7458inline elements enclosed by square brackets (`[` and `]`). The
7459following rules apply:
7460
7461- Links may not contain other links, at any level of nesting. If
7462 multiple otherwise valid link definitions appear nested inside each
7463 other, the inner-most definition is used.
7464
7465- Brackets are allowed in the [link text] only if (a) they
7466 are backslash-escaped or (b) they appear as a matched pair of brackets,
7467 with an open bracket `[`, a sequence of zero or more inlines, and
7468 a close bracket `]`.
7469
7470- Backtick [code spans], [autolinks], and raw [HTML tags] bind more tightly
7471 than the brackets in link text. Thus, for example,
7472 `` [foo`]` `` could not be a link text, since the second `]`
7473 is part of a code span.
7474
7475- The brackets in link text bind more tightly than markers for
7476 [emphasis and strong emphasis]. Thus, for example, `*[foo*](url)` is a link.
7477
7478A [link destination](@) consists of either
7479
7480- a sequence of zero or more characters between an opening `<` and a
7481 closing `>` that contains no line endings or unescaped
7482 `<` or `>` characters, or
7483
7484- a nonempty sequence of characters that does not start with `<`,
7485 does not include [ASCII control characters][ASCII control character]
7486 or [space] character, and includes parentheses only if (a) they are
7487 backslash-escaped or (b) they are part of a balanced pair of
7488 unescaped parentheses.
7489 (Implementations may impose limits on parentheses nesting to
7490 avoid performance issues, but at least three levels of nesting
7491 should be supported.)
7492
7493A [link title](@) consists of either
7494
7495- a sequence of zero or more characters between straight double-quote
7496 characters (`"`), including a `"` character only if it is
7497 backslash-escaped, or
7498
7499- a sequence of zero or more characters between straight single-quote
7500 characters (`'`), including a `'` character only if it is
7501 backslash-escaped, or
7502
7503- a sequence of zero or more characters between matching parentheses
7504 (`(...)`), including a `(` or `)` character only if it is
7505 backslash-escaped.
7506
7507Although [link titles] may span multiple lines, they may not contain
7508a [blank line].
7509
7510An [inline link](@) consists of a [link text] followed immediately
7511by a left parenthesis `(`, an optional [link destination], an optional
7512[link title], and a right parenthesis `)`.
7513These four components may be separated by spaces, tabs, and up to one line
7514ending.
7515If both [link destination] and [link title] are present, they *must* be
7516separated by spaces, tabs, and up to one line ending.
7517
7518The link's text consists of the inlines contained
7519in the [link text] (excluding the enclosing square brackets).
7520The link's URI consists of the link destination, excluding enclosing
7521`<...>` if present, with backslash-escapes in effect as described
7522above. The link's title consists of the link title, excluding its
7523enclosing delimiters, with backslash-escapes in effect as described
7524above.
7525
7526Here is a simple inline link:
7527
7528```````````````````````````````` example
7529[link](/uri "title")
7530.
7531<p><a href="/uri" title="title">link</a></p>
7532````````````````````````````````
7533
7534
7535The title, the link text and even
7536the destination may be omitted:
7537
7538```````````````````````````````` example
7539[link](/uri)
7540.
7541<p><a href="/uri">link</a></p>
7542````````````````````````````````
7543
7544```````````````````````````````` example
7545[](./target.md)
7546.
7547<p><a href="./target.md"></a></p>
7548````````````````````````````````
7549
7550
7551```````````````````````````````` example
7552[link]()
7553.
7554<p><a href="">link</a></p>
7555````````````````````````````````
7556
7557
7558```````````````````````````````` example
7559[link](<>)
7560.
7561<p><a href="">link</a></p>
7562````````````````````````````````
7563
7564
7565```````````````````````````````` example
7566[]()
7567.
7568<p><a href=""></a></p>
7569````````````````````````````````
7570
7571The destination can only contain spaces if it is
7572enclosed in pointy brackets:
7573
7574```````````````````````````````` example
7575[link](/my uri)
7576.
7577<p>[link](/my uri)</p>
7578````````````````````````````````
7579
7580```````````````````````````````` example
7581[link](</my uri>)
7582.
7583<p><a href="/my%20uri">link</a></p>
7584````````````````````````````````
7585
7586The destination cannot contain line endings,
7587even if enclosed in pointy brackets:
7588
7589```````````````````````````````` example
7590[link](foo
7591bar)
7592.
7593<p>[link](foo
7594bar)</p>
7595````````````````````````````````
7596
7597```````````````````````````````` example
7598[link](<foo
7599bar>)
7600.
7601<p>[link](<foo
7602bar>)</p>
7603````````````````````````````````
7604
7605The destination can contain `)` if it is enclosed
7606in pointy brackets:
7607
7608```````````````````````````````` example
7609[a](<b)c>)
7610.
7611<p><a href="b)c">a</a></p>
7612````````````````````````````````
7613
7614Pointy brackets that enclose links must be unescaped:
7615
7616```````````````````````````````` example
7617[link](<foo\>)
7618.
7619<p>[link](&lt;foo&gt;)</p>
7620````````````````````````````````
7621
7622These are not links, because the opening pointy bracket
7623is not matched properly:
7624
7625```````````````````````````````` example
7626[a](<b)c
7627[a](<b)c>
7628[a](<b>c)
7629.
7630<p>[a](&lt;b)c
7631[a](&lt;b)c&gt;
7632[a](<b>c)</p>
7633````````````````````````````````
7634
7635Parentheses inside the link destination may be escaped:
7636
7637```````````````````````````````` example
7638[link](\(foo\))
7639.
7640<p><a href="(foo)">link</a></p>
7641````````````````````````````````
7642
7643Any number of parentheses are allowed without escaping, as long as they are
7644balanced:
7645
7646```````````````````````````````` example
7647[link](foo(and(bar)))
7648.
7649<p><a href="foo(and(bar))">link</a></p>
7650````````````````````````````````
7651
7652However, if you have unbalanced parentheses, you need to escape or use the
7653`<...>` form:
7654
7655```````````````````````````````` example
7656[link](foo(and(bar))
7657.
7658<p>[link](foo(and(bar))</p>
7659````````````````````````````````
7660
7661
7662```````````````````````````````` example
7663[link](foo\(and\(bar\))
7664.
7665<p><a href="foo(and(bar)">link</a></p>
7666````````````````````````````````
7667
7668
7669```````````````````````````````` example
7670[link](<foo(and(bar)>)
7671.
7672<p><a href="foo(and(bar)">link</a></p>
7673````````````````````````````````
7674
7675
7676Parentheses and other symbols can also be escaped, as usual
7677in Markdown:
7678
7679```````````````````````````````` example
7680[link](foo\)\:)
7681.
7682<p><a href="foo):">link</a></p>
7683````````````````````````````````
7684
7685
7686A link can contain fragment identifiers and queries:
7687
7688```````````````````````````````` example
7689[link](#fragment)
7690
7691[link](http://example.com#fragment)
7692
7693[link](http://example.com?foo=3#frag)
7694.
7695<p><a href="#fragment">link</a></p>
7696<p><a href="http://example.com#fragment">link</a></p>
7697<p><a href="http://example.com?foo=3#frag">link</a></p>
7698````````````````````````````````
7699
7700
7701Note that a backslash before a non-escapable character is
7702just a backslash:
7703
7704```````````````````````````````` example
7705[link](foo\bar)
7706.
7707<p><a href="foo%5Cbar">link</a></p>
7708````````````````````````````````
7709
7710
7711URL-escaping should be left alone inside the destination, as all
7712URL-escaped characters are also valid URL characters. Entity and
7713numerical character references in the destination will be parsed
7714into the corresponding Unicode code points, as usual. These may
7715be optionally URL-escaped when written as HTML, but this spec
7716does not enforce any particular policy for rendering URLs in
7717HTML or other formats. Renderers may make different decisions
7718about how to escape or normalize URLs in the output.
7719
7720```````````````````````````````` example
7721[link](foo%20b&auml;)
7722.
7723<p><a href="foo%20b%C3%A4">link</a></p>
7724````````````````````````````````
7725
7726
7727Note that, because titles can often be parsed as destinations,
7728if you try to omit the destination and keep the title, you'll
7729get unexpected results:
7730
7731```````````````````````````````` example
7732[link]("title")
7733.
7734<p><a href="%22title%22">link</a></p>
7735````````````````````````````````
7736
7737
7738Titles may be in single quotes, double quotes, or parentheses:
7739
7740```````````````````````````````` example
7741[link](/url "title")
7742[link](/url 'title')
7743[link](/url (title))
7744.
7745<p><a href="/url" title="title">link</a>
7746<a href="/url" title="title">link</a>
7747<a href="/url" title="title">link</a></p>
7748````````````````````````````````
7749
7750
7751Backslash escapes and entity and numeric character references
7752may be used in titles:
7753
7754```````````````````````````````` example
7755[link](/url "title \"&quot;")
7756.
7757<p><a href="/url" title="title &quot;&quot;">link</a></p>
7758````````````````````````````````
7759
7760
7761Titles must be separated from the link using spaces, tabs, and up to one line
7762ending.
7763Other [Unicode whitespace] like non-breaking space doesn't work.
7764
7765```````````````````````````````` example
7766[link](/url "title")
7767.
7768<p><a href="/url%C2%A0%22title%22">link</a></p>
7769````````````````````````````````
7770
7771
7772Nested balanced quotes are not allowed without escaping:
7773
7774```````````````````````````````` example
7775[link](/url "title "and" title")
7776.
7777<p>[link](/url &quot;title &quot;and&quot; title&quot;)</p>
7778````````````````````````````````
7779
7780
7781But it is easy to work around this by using a different quote type:
7782
7783```````````````````````````````` example
7784[link](/url 'title "and" title')
7785.
7786<p><a href="/url" title="title &quot;and&quot; title">link</a></p>
7787````````````````````````````````
7788
7789
7790(Note: `Markdown.pl` did allow double quotes inside a double-quoted
7791title, and its test suite included a test demonstrating this.
7792But it is hard to see a good rationale for the extra complexity this
7793brings, since there are already many ways---backslash escaping,
7794entity and numeric character references, or using a different
7795quote type for the enclosing title---to write titles containing
7796double quotes. `Markdown.pl`'s handling of titles has a number
7797of other strange features. For example, it allows single-quoted
7798titles in inline links, but not reference links. And, in
7799reference links but not inline links, it allows a title to begin
7800with `"` and end with `)`. `Markdown.pl` 1.0.1 even allows
7801titles with no closing quotation mark, though 1.0.2b8 does not.
7802It seems preferable to adopt a simple, rational rule that works
7803the same way in inline links and link reference definitions.)
7804
7805Spaces, tabs, and up to one line ending is allowed around the destination and
7806title:
7807
7808```````````````````````````````` example
7809[link]( /uri
7810 "title" )
7811.
7812<p><a href="/uri" title="title">link</a></p>
7813````````````````````````````````
7814
7815
7816But it is not allowed between the link text and the
7817following parenthesis:
7818
7819```````````````````````````````` example
7820[link] (/uri)
7821.
7822<p>[link] (/uri)</p>
7823````````````````````````````````
7824
7825
7826The link text may contain balanced brackets, but not unbalanced ones,
7827unless they are escaped:
7828
7829```````````````````````````````` example
7830[link [foo [bar]]](/uri)
7831.
7832<p><a href="/uri">link [foo [bar]]</a></p>
7833````````````````````````````````
7834
7835
7836```````````````````````````````` example
7837[link] bar](/uri)
7838.
7839<p>[link] bar](/uri)</p>
7840````````````````````````````````
7841
7842
7843```````````````````````````````` example
7844[link [bar](/uri)
7845.
7846<p>[link <a href="/uri">bar</a></p>
7847````````````````````````````````
7848
7849
7850```````````````````````````````` example
7851[link \[bar](/uri)
7852.
7853<p><a href="/uri">link [bar</a></p>
7854````````````````````````````````
7855
7856
7857The link text may contain inline content:
7858
7859```````````````````````````````` example
7860[link *foo **bar** `#`*](/uri)
7861.
7862<p><a href="/uri">link <em>foo <strong>bar</strong> <code>#</code></em></a></p>
7863````````````````````````````````
7864
7865
7866```````````````````````````````` example
7867[![moon](moon.jpg)](/uri)
7868.
7869<p><a href="/uri"><img src="moon.jpg" alt="moon" /></a></p>
7870````````````````````````````````
7871
7872
7873However, links may not contain other links, at any level of nesting.
7874
7875```````````````````````````````` example
7876[foo [bar](/uri)](/uri)
7877.
7878<p>[foo <a href="/uri">bar</a>](/uri)</p>
7879````````````````````````````````
7880
7881
7882```````````````````````````````` example
7883[foo *[bar [baz](/uri)](/uri)*](/uri)
7884.
7885<p>[foo <em>[bar <a href="/uri">baz</a>](/uri)</em>](/uri)</p>
7886````````````````````````````````
7887
7888
7889```````````````````````````````` example
7890![[[foo](uri1)](uri2)](uri3)
7891.
7892<p><img src="uri3" alt="[foo](uri2)" /></p>
7893````````````````````````````````
7894
7895
7896These cases illustrate the precedence of link text grouping over
7897emphasis grouping:
7898
7899```````````````````````````````` example
7900*[foo*](/uri)
7901.
7902<p>*<a href="/uri">foo*</a></p>
7903````````````````````````````````
7904
7905
7906```````````````````````````````` example
7907[foo *bar](baz*)
7908.
7909<p><a href="baz*">foo *bar</a></p>
7910````````````````````````````````
7911
7912
7913Note that brackets that *aren't* part of links do not take
7914precedence:
7915
7916```````````````````````````````` example
7917*foo [bar* baz]
7918.
7919<p><em>foo [bar</em> baz]</p>
7920````````````````````````````````
7921
7922
7923These cases illustrate the precedence of HTML tags, code spans,
7924and autolinks over link grouping:
7925
7926```````````````````````````````` example
7927[foo <bar attr="](baz)">
7928.
7929<p>[foo <bar attr="](baz)"></p>
7930````````````````````````````````
7931
7932
7933```````````````````````````````` example
7934[foo`](/uri)`
7935.
7936<p>[foo<code>](/uri)</code></p>
7937````````````````````````````````
7938
7939
7940```````````````````````````````` example
7941[foo<http://example.com/?search=](uri)>
7942.
7943<p>[foo<a href="http://example.com/?search=%5D(uri)">http://example.com/?search=](uri)</a></p>
7944````````````````````````````````
7945
7946
7947There are three kinds of [reference link](@)s:
7948[full](#full-reference-link), [collapsed](#collapsed-reference-link),
7949and [shortcut](#shortcut-reference-link).
7950
7951A [full reference link](@)
7952consists of a [link text] immediately followed by a [link label]
7953that [matches] a [link reference definition] elsewhere in the document.
7954
7955A [link label](@) begins with a left bracket (`[`) and ends
7956with the first right bracket (`]`) that is not backslash-escaped.
7957Between these brackets there must be at least one character that is not a space,
7958tab, or line ending.
7959Unescaped square bracket characters are not allowed inside the
7960opening and closing square brackets of [link labels]. A link
7961label can have at most 999 characters inside the square
7962brackets.
7963
7964One label [matches](@)
7965another just in case their normalized forms are equal. To normalize a
7966label, strip off the opening and closing brackets,
7967perform the *Unicode case fold*, strip leading and trailing
7968spaces, tabs, and line endings, and collapse consecutive internal
7969spaces, tabs, and line endings to a single space. If there are multiple
7970matching reference link definitions, the one that comes first in the
7971document is used. (It is desirable in such cases to emit a warning.)
7972
7973The link's URI and title are provided by the matching [link
7974reference definition].
7975
7976Here is a simple example:
7977
7978```````````````````````````````` example
7979[foo][bar]
7980
7981[bar]: /url "title"
7982.
7983<p><a href="/url" title="title">foo</a></p>
7984````````````````````````````````
7985
7986
7987The rules for the [link text] are the same as with
7988[inline links]. Thus:
7989
7990The link text may contain balanced brackets, but not unbalanced ones,
7991unless they are escaped:
7992
7993```````````````````````````````` example
7994[link [foo [bar]]][ref]
7995
7996[ref]: /uri
7997.
7998<p><a href="/uri">link [foo [bar]]</a></p>
7999````````````````````````````````
8000
8001
8002```````````````````````````````` example
8003[link \[bar][ref]
8004
8005[ref]: /uri
8006.
8007<p><a href="/uri">link [bar</a></p>
8008````````````````````````````````
8009
8010
8011The link text may contain inline content:
8012
8013```````````````````````````````` example
8014[link *foo **bar** `#`*][ref]
8015
8016[ref]: /uri
8017.
8018<p><a href="/uri">link <em>foo <strong>bar</strong> <code>#</code></em></a></p>
8019````````````````````````````````
8020
8021
8022```````````````````````````````` example
8023[![moon](moon.jpg)][ref]
8024
8025[ref]: /uri
8026.
8027<p><a href="/uri"><img src="moon.jpg" alt="moon" /></a></p>
8028````````````````````````````````
8029
8030
8031However, links may not contain other links, at any level of nesting.
8032
8033```````````````````````````````` example
8034[foo [bar](/uri)][ref]
8035
8036[ref]: /uri
8037.
8038<p>[foo <a href="/uri">bar</a>]<a href="/uri">ref</a></p>
8039````````````````````````````````
8040
8041
8042```````````````````````````````` example
8043[foo *bar [baz][ref]*][ref]
8044
8045[ref]: /uri
8046.
8047<p>[foo <em>bar <a href="/uri">baz</a></em>]<a href="/uri">ref</a></p>
8048````````````````````````````````
8049
8050
8051(In the examples above, we have two [shortcut reference links]
8052instead of one [full reference link].)
8053
8054The following cases illustrate the precedence of link text grouping over
8055emphasis grouping:
8056
8057```````````````````````````````` example
8058*[foo*][ref]
8059
8060[ref]: /uri
8061.
8062<p>*<a href="/uri">foo*</a></p>
8063````````````````````````````````
8064
8065
8066```````````````````````````````` example
8067[foo *bar][ref]*
8068
8069[ref]: /uri
8070.
8071<p><a href="/uri">foo *bar</a>*</p>
8072````````````````````````````````
8073
8074
8075These cases illustrate the precedence of HTML tags, code spans,
8076and autolinks over link grouping:
8077
8078```````````````````````````````` example
8079[foo <bar attr="][ref]">
8080
8081[ref]: /uri
8082.
8083<p>[foo <bar attr="][ref]"></p>
8084````````````````````````````````
8085
8086
8087```````````````````````````````` example
8088[foo`][ref]`
8089
8090[ref]: /uri
8091.
8092<p>[foo<code>][ref]</code></p>
8093````````````````````````````````
8094
8095
8096```````````````````````````````` example
8097[foo<http://example.com/?search=][ref]>
8098
8099[ref]: /uri
8100.
8101<p>[foo<a href="http://example.com/?search=%5D%5Bref%5D">http://example.com/?search=][ref]</a></p>
8102````````````````````````````````
8103
8104
8105Matching is case-insensitive:
8106
8107```````````````````````````````` example
8108[foo][BaR]
8109
8110[bar]: /url "title"
8111.
8112<p><a href="/url" title="title">foo</a></p>
8113````````````````````````````````
8114
8115
8116Unicode case fold is used:
8117
8118```````````````````````````````` example
8119[ẞ]
8120
8121[SS]: /url
8122.
8123<p><a href="/url">ẞ</a></p>
8124````````````````````````````````
8125
8126
8127Consecutive internal spaces, tabs, and line endings are treated as one space for
8128purposes of determining matching:
8129
8130```````````````````````````````` example
8131[Foo
8132 bar]: /url
8133
8134[Baz][Foo bar]
8135.
8136<p><a href="/url">Baz</a></p>
8137````````````````````````````````
8138
8139
8140No spaces, tabs, or line endings are allowed between the [link text] and the
8141[link label]:
8142
8143```````````````````````````````` example
8144[foo] [bar]
8145
8146[bar]: /url "title"
8147.
8148<p>[foo] <a href="/url" title="title">bar</a></p>
8149````````````````````````````````
8150
8151
8152```````````````````````````````` example
8153[foo]
8154[bar]
8155
8156[bar]: /url "title"
8157.
8158<p>[foo]
8159<a href="/url" title="title">bar</a></p>
8160````````````````````````````````
8161
8162
8163This is a departure from John Gruber's original Markdown syntax
8164description, which explicitly allows whitespace between the link
8165text and the link label. It brings reference links in line with
8166[inline links], which (according to both original Markdown and
8167this spec) cannot have whitespace after the link text. More
8168importantly, it prevents inadvertent capture of consecutive
8169[shortcut reference links]. If whitespace is allowed between the
8170link text and the link label, then in the following we will have
8171a single reference link, not two shortcut reference links, as
8172intended:
8173
8174``` markdown
8175[foo]
8176[bar]
8177
8178[foo]: /url1
8179[bar]: /url2
8180```
8181
8182(Note that [shortcut reference links] were introduced by Gruber
8183himself in a beta version of `Markdown.pl`, but never included
8184in the official syntax description. Without shortcut reference
8185links, it is harmless to allow space between the link text and
8186link label; but once shortcut references are introduced, it is
8187too dangerous to allow this, as it frequently leads to
8188unintended results.)
8189
8190When there are multiple matching [link reference definitions],
8191the first is used:
8192
8193```````````````````````````````` example
8194[foo]: /url1
8195
8196[foo]: /url2
8197
8198[bar][foo]
8199.
8200<p><a href="/url1">bar</a></p>
8201````````````````````````````````
8202
8203
8204Note that matching is performed on normalized strings, not parsed
8205inline content. So the following does not match, even though the
8206labels define equivalent inline content:
8207
8208```````````````````````````````` example
8209[bar][foo\!]
8210
8211[foo!]: /url
8212.
8213<p>[bar][foo!]</p>
8214````````````````````````````````
8215
8216
8217[Link labels] cannot contain brackets, unless they are
8218backslash-escaped:
8219
8220```````````````````````````````` example
8221[foo][ref[]
8222
8223[ref[]: /uri
8224.
8225<p>[foo][ref[]</p>
8226<p>[ref[]: /uri</p>
8227````````````````````````````````
8228
8229
8230```````````````````````````````` example
8231[foo][ref[bar]]
8232
8233[ref[bar]]: /uri
8234.
8235<p>[foo][ref[bar]]</p>
8236<p>[ref[bar]]: /uri</p>
8237````````````````````````````````
8238
8239
8240```````````````````````````````` example
8241[[[foo]]]
8242
8243[[[foo]]]: /url
8244.
8245<p>[[[foo]]]</p>
8246<p>[[[foo]]]: /url</p>
8247````````````````````````````````
8248
8249
8250```````````````````````````````` example
8251[foo][ref\[]
8252
8253[ref\[]: /uri
8254.
8255<p><a href="/uri">foo</a></p>
8256````````````````````````````````
8257
8258
8259Note that in this example `]` is not backslash-escaped:
8260
8261```````````````````````````````` example
8262[bar\\]: /uri
8263
8264[bar\\]
8265.
8266<p><a href="/uri">bar\</a></p>
8267````````````````````````````````
8268
8269
8270A [link label] must contain at least one character that is not a space, tab, or
8271line ending:
8272
8273```````````````````````````````` example
8274[]
8275
8276[]: /uri
8277.
8278<p>[]</p>
8279<p>[]: /uri</p>
8280````````````````````````````````
8281
8282
8283```````````````````````````````` example
8284[
8285 ]
8286
8287[
8288 ]: /uri
8289.
8290<p>[
8291]</p>
8292<p>[
8293]: /uri</p>
8294````````````````````````````````
8295
8296
8297A [collapsed reference link](@)
8298consists of a [link label] that [matches] a
8299[link reference definition] elsewhere in the
8300document, followed by the string `[]`.
8301The contents of the first link label are parsed as inlines,
8302which are used as the link's text. The link's URI and title are
8303provided by the matching reference link definition. Thus,
8304`[foo][]` is equivalent to `[foo][foo]`.
8305
8306```````````````````````````````` example
8307[foo][]
8308
8309[foo]: /url "title"
8310.
8311<p><a href="/url" title="title">foo</a></p>
8312````````````````````````````````
8313
8314
8315```````````````````````````````` example
8316[*foo* bar][]
8317
8318[*foo* bar]: /url "title"
8319.
8320<p><a href="/url" title="title"><em>foo</em> bar</a></p>
8321````````````````````````````````
8322
8323
8324The link labels are case-insensitive:
8325
8326```````````````````````````````` example
8327[Foo][]
8328
8329[foo]: /url "title"
8330.
8331<p><a href="/url" title="title">Foo</a></p>
8332````````````````````````````````
8333
8334
8335
8336As with full reference links, spaces, tabs, or line endings are not
8337allowed between the two sets of brackets:
8338
8339```````````````````````````````` example
8340[foo]
8341[]
8342
8343[foo]: /url "title"
8344.
8345<p><a href="/url" title="title">foo</a>
8346[]</p>
8347````````````````````````````````
8348
8349
8350A [shortcut reference link](@)
8351consists of a [link label] that [matches] a
8352[link reference definition] elsewhere in the
8353document and is not followed by `[]` or a link label.
8354The contents of the first link label are parsed as inlines,
8355which are used as the link's text. The link's URI and title
8356are provided by the matching link reference definition.
8357Thus, `[foo]` is equivalent to `[foo][]`.
8358
8359```````````````````````````````` example
8360[foo]
8361
8362[foo]: /url "title"
8363.
8364<p><a href="/url" title="title">foo</a></p>
8365````````````````````````````````
8366
8367
8368```````````````````````````````` example
8369[*foo* bar]
8370
8371[*foo* bar]: /url "title"
8372.
8373<p><a href="/url" title="title"><em>foo</em> bar</a></p>
8374````````````````````````````````
8375
8376
8377```````````````````````````````` example
8378[[*foo* bar]]
8379
8380[*foo* bar]: /url "title"
8381.
8382<p>[<a href="/url" title="title"><em>foo</em> bar</a>]</p>
8383````````````````````````````````
8384
8385
8386```````````````````````````````` example
8387[[bar [foo]
8388
8389[foo]: /url
8390.
8391<p>[[bar <a href="/url">foo</a></p>
8392````````````````````````````````
8393
8394
8395The link labels are case-insensitive:
8396
8397```````````````````````````````` example
8398[Foo]
8399
8400[foo]: /url "title"
8401.
8402<p><a href="/url" title="title">Foo</a></p>
8403````````````````````````````````
8404
8405
8406A space after the link text should be preserved:
8407
8408```````````````````````````````` example
8409[foo] bar
8410
8411[foo]: /url
8412.
8413<p><a href="/url">foo</a> bar</p>
8414````````````````````````````````
8415
8416
8417If you just want bracketed text, you can backslash-escape the
8418opening bracket to avoid links:
8419
8420```````````````````````````````` example
8421\[foo]
8422
8423[foo]: /url "title"
8424.
8425<p>[foo]</p>
8426````````````````````````````````
8427
8428
8429Note that this is a link, because a link label ends with the first
8430following closing bracket:
8431
8432```````````````````````````````` example
8433[foo*]: /url
8434
8435*[foo*]
8436.
8437<p>*<a href="/url">foo*</a></p>
8438````````````````````````````````
8439
8440
8441Full and compact references take precedence over shortcut
8442references:
8443
8444```````````````````````````````` example
8445[foo][bar]
8446
8447[foo]: /url1
8448[bar]: /url2
8449.
8450<p><a href="/url2">foo</a></p>
8451````````````````````````````````
8452
8453```````````````````````````````` example
8454[foo][]
8455
8456[foo]: /url1
8457.
8458<p><a href="/url1">foo</a></p>
8459````````````````````````````````
8460
8461Inline links also take precedence:
8462
8463```````````````````````````````` example
8464[foo]()
8465
8466[foo]: /url1
8467.
8468<p><a href="">foo</a></p>
8469````````````````````````````````
8470
8471```````````````````````````````` example
8472[foo](not a link)
8473
8474[foo]: /url1
8475.
8476<p><a href="/url1">foo</a>(not a link)</p>
8477````````````````````````````````
8478
8479In the following case `[bar][baz]` is parsed as a reference,
8480`[foo]` as normal text:
8481
8482```````````````````````````````` example
8483[foo][bar][baz]
8484
8485[baz]: /url
8486.
8487<p>[foo]<a href="/url">bar</a></p>
8488````````````````````````````````
8489
8490
8491Here, though, `[foo][bar]` is parsed as a reference, since
8492`[bar]` is defined:
8493
8494```````````````````````````````` example
8495[foo][bar][baz]
8496
8497[baz]: /url1
8498[bar]: /url2
8499.
8500<p><a href="/url2">foo</a><a href="/url1">baz</a></p>
8501````````````````````````````````
8502
8503
8504Here `[foo]` is not parsed as a shortcut reference, because it
8505is followed by a link label (even though `[bar]` is not defined):
8506
8507```````````````````````````````` example
8508[foo][bar][baz]
8509
8510[baz]: /url1
8511[foo]: /url2
8512.
8513<p>[foo]<a href="/url1">bar</a></p>
8514````````````````````````````````
8515
8516
8517
8518## Images
8519
8520Syntax for images is like the syntax for links, with one
8521difference. Instead of [link text], we have an
8522[image description](@). The rules for this are the
8523same as for [link text], except that (a) an
8524image description starts with `![` rather than `[`, and
8525(b) an image description may contain links.
8526An image description has inline elements
8527as its contents. When an image is rendered to HTML,
8528this is standardly used as the image's `alt` attribute.
8529
8530```````````````````````````````` example
8531![foo](/url "title")
8532.
8533<p><img src="/url" alt="foo" title="title" /></p>
8534````````````````````````````````
8535
8536
8537```````````````````````````````` example
8538![foo *bar*]
8539
8540[foo *bar*]: train.jpg "train & tracks"
8541.
8542<p><img src="train.jpg" alt="foo bar" title="train &amp; tracks" /></p>
8543````````````````````````````````
8544
8545
8546```````````````````````````````` example
8547![foo ![bar](/url)](/url2)
8548.
8549<p><img src="/url2" alt="foo bar" /></p>
8550````````````````````````````````
8551
8552
8553```````````````````````````````` example
8554![foo [bar](/url)](/url2)
8555.
8556<p><img src="/url2" alt="foo bar" /></p>
8557````````````````````````````````
8558
8559
8560Though this spec is concerned with parsing, not rendering, it is
8561recommended that in rendering to HTML, only the plain string content
8562of the [image description] be used. Note that in
8563the above example, the alt attribute's value is `foo bar`, not `foo
8564[bar](/url)` or `foo <a href="/url">bar</a>`. Only the plain string
8565content is rendered, without formatting.
8566
8567```````````````````````````````` example
8568![foo *bar*][]
8569
8570[foo *bar*]: train.jpg "train & tracks"
8571.
8572<p><img src="train.jpg" alt="foo bar" title="train &amp; tracks" /></p>
8573````````````````````````````````
8574
8575
8576```````````````````````````````` example
8577![foo *bar*][foobar]
8578
8579[FOOBAR]: train.jpg "train & tracks"
8580.
8581<p><img src="train.jpg" alt="foo bar" title="train &amp; tracks" /></p>
8582````````````````````````````````
8583
8584
8585```````````````````````````````` example
8586![foo](train.jpg)
8587.
8588<p><img src="train.jpg" alt="foo" /></p>
8589````````````````````````````````
8590
8591
8592```````````````````````````````` example
8593My ![foo bar](/path/to/train.jpg "title" )
8594.
8595<p>My <img src="/path/to/train.jpg" alt="foo bar" title="title" /></p>
8596````````````````````````````````
8597
8598
8599```````````````````````````````` example
8600![foo](<url>)
8601.
8602<p><img src="url" alt="foo" /></p>
8603````````````````````````````````
8604
8605
8606```````````````````````````````` example
8607![](/url)
8608.
8609<p><img src="/url" alt="" /></p>
8610````````````````````````````````
8611
8612
8613Reference-style:
8614
8615```````````````````````````````` example
8616![foo][bar]
8617
8618[bar]: /url
8619.
8620<p><img src="/url" alt="foo" /></p>
8621````````````````````````````````
8622
8623
8624```````````````````````````````` example
8625![foo][bar]
8626
8627[BAR]: /url
8628.
8629<p><img src="/url" alt="foo" /></p>
8630````````````````````````````````
8631
8632
8633Collapsed:
8634
8635```````````````````````````````` example
8636![foo][]
8637
8638[foo]: /url "title"
8639.
8640<p><img src="/url" alt="foo" title="title" /></p>
8641````````````````````````````````
8642
8643
8644```````````````````````````````` example
8645![*foo* bar][]
8646
8647[*foo* bar]: /url "title"
8648.
8649<p><img src="/url" alt="foo bar" title="title" /></p>
8650````````````````````````````````
8651
8652
8653The labels are case-insensitive:
8654
8655```````````````````````````````` example
8656![Foo][]
8657
8658[foo]: /url "title"
8659.
8660<p><img src="/url" alt="Foo" title="title" /></p>
8661````````````````````````````````
8662
8663
8664As with reference links, spaces, tabs, and line endings, are not allowed
8665between the two sets of brackets:
8666
8667```````````````````````````````` example
8668![foo]
8669[]
8670
8671[foo]: /url "title"
8672.
8673<p><img src="/url" alt="foo" title="title" />
8674[]</p>
8675````````````````````````````````
8676
8677
8678Shortcut:
8679
8680```````````````````````````````` example
8681![foo]
8682
8683[foo]: /url "title"
8684.
8685<p><img src="/url" alt="foo" title="title" /></p>
8686````````````````````````````````
8687
8688
8689```````````````````````````````` example
8690![*foo* bar]
8691
8692[*foo* bar]: /url "title"
8693.
8694<p><img src="/url" alt="foo bar" title="title" /></p>
8695````````````````````````````````
8696
8697
8698Note that link labels cannot contain unescaped brackets:
8699
8700```````````````````````````````` example
8701![[foo]]
8702
8703[[foo]]: /url "title"
8704.
8705<p>![[foo]]</p>
8706<p>[[foo]]: /url &quot;title&quot;</p>
8707````````````````````````````````
8708
8709
8710The link labels are case-insensitive:
8711
8712```````````````````````````````` example
8713![Foo]
8714
8715[foo]: /url "title"
8716.
8717<p><img src="/url" alt="Foo" title="title" /></p>
8718````````````````````````````````
8719
8720
8721If you just want a literal `!` followed by bracketed text, you can
8722backslash-escape the opening `[`:
8723
8724```````````````````````````````` example
8725!\[foo]
8726
8727[foo]: /url "title"
8728.
8729<p>![foo]</p>
8730````````````````````````````````
8731
8732
8733If you want a link after a literal `!`, backslash-escape the
8734`!`:
8735
8736```````````````````````````````` example
8737\![foo]
8738
8739[foo]: /url "title"
8740.
8741<p>!<a href="/url" title="title">foo</a></p>
8742````````````````````````````````
8743
8744
8745## Autolinks
8746
8747[Autolink](@)s are absolute URIs and email addresses inside
8748`<` and `>`. They are parsed as links, with the URL or email address
8749as the link label.
8750
8751A [URI autolink](@) consists of `<`, followed by an
8752[absolute URI] followed by `>`. It is parsed as
8753a link to the URI, with the URI as the link's label.
8754
8755An [absolute URI](@),
8756for these purposes, consists of a [scheme] followed by a colon (`:`)
8757followed by zero or more characters other [ASCII control
8758characters][ASCII control character], [space], `<`, and `>`.
8759If the URI includes these characters, they must be percent-encoded
8760(e.g. `%20` for a space).
8761
8762For purposes of this spec, a [scheme](@) is any sequence
8763of 2--32 characters beginning with an ASCII letter and followed
8764by any combination of ASCII letters, digits, or the symbols plus
8765("+"), period ("."), or hyphen ("-").
8766
8767Here are some valid autolinks:
8768
8769```````````````````````````````` example
8770<http://foo.bar.baz>
8771.
8772<p><a href="http://foo.bar.baz">http://foo.bar.baz</a></p>
8773````````````````````````````````
8774
8775
8776```````````````````````````````` example
8777<http://foo.bar.baz/test?q=hello&id=22&boolean>
8778.
8779<p><a href="http://foo.bar.baz/test?q=hello&amp;id=22&amp;boolean">http://foo.bar.baz/test?q=hello&amp;id=22&amp;boolean</a></p>
8780````````````````````````````````
8781
8782
8783```````````````````````````````` example
8784<irc://foo.bar:2233/baz>
8785.
8786<p><a href="irc://foo.bar:2233/baz">irc://foo.bar:2233/baz</a></p>
8787````````````````````````````````
8788
8789
8790Uppercase is also fine:
8791
8792```````````````````````````````` example
8793<MAILTO:FOO@BAR.BAZ>
8794.
8795<p><a href="MAILTO:FOO@BAR.BAZ">MAILTO:FOO@BAR.BAZ</a></p>
8796````````````````````````````````
8797
8798
8799Note that many strings that count as [absolute URIs] for
8800purposes of this spec are not valid URIs, because their
8801schemes are not registered or because of other problems
8802with their syntax:
8803
8804```````````````````````````````` example
8805<a+b+c:d>
8806.
8807<p><a href="a+b+c:d">a+b+c:d</a></p>
8808````````````````````````````````
8809
8810
8811```````````````````````````````` example
8812<made-up-scheme://foo,bar>
8813.
8814<p><a href="made-up-scheme://foo,bar">made-up-scheme://foo,bar</a></p>
8815````````````````````````````````
8816
8817
8818```````````````````````````````` example
8819<http://../>
8820.
8821<p><a href="http://../">http://../</a></p>
8822````````````````````````````````
8823
8824
8825```````````````````````````````` example
8826<localhost:5001/foo>
8827.
8828<p><a href="localhost:5001/foo">localhost:5001/foo</a></p>
8829````````````````````````````````
8830
8831
8832Spaces are not allowed in autolinks:
8833
8834```````````````````````````````` example
8835<http://foo.bar/baz bim>
8836.
8837<p>&lt;http://foo.bar/baz bim&gt;</p>
8838````````````````````````````````
8839
8840
8841Backslash-escapes do not work inside autolinks:
8842
8843```````````````````````````````` example
8844<http://example.com/\[\>
8845.
8846<p><a href="http://example.com/%5C%5B%5C">http://example.com/\[\</a></p>
8847````````````````````````````````
8848
8849
8850An [email autolink](@)
8851consists of `<`, followed by an [email address],
8852followed by `>`. The link's label is the email address,
8853and the URL is `mailto:` followed by the email address.
8854
8855An [email address](@),
8856for these purposes, is anything that matches
8857the [non-normative regex from the HTML5
8858spec](https://html.spec.whatwg.org/multipage/forms.html#e-mail-state-(type=email)):
8859
8860 /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?
8861 (?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
8862
8863Examples of email autolinks:
8864
8865```````````````````````````````` example
8866<foo@bar.example.com>
8867.
8868<p><a href="mailto:foo@bar.example.com">foo@bar.example.com</a></p>
8869````````````````````````````````
8870
8871
8872```````````````````````````````` example
8873<foo+special@Bar.baz-bar0.com>
8874.
8875<p><a href="mailto:foo+special@Bar.baz-bar0.com">foo+special@Bar.baz-bar0.com</a></p>
8876````````````````````````````````
8877
8878
8879Backslash-escapes do not work inside email autolinks:
8880
8881```````````````````````````````` example
8882<foo\+@bar.example.com>
8883.
8884<p>&lt;foo+@bar.example.com&gt;</p>
8885````````````````````````````````
8886
8887
8888These are not autolinks:
8889
8890```````````````````````````````` example
8891<>
8892.
8893<p>&lt;&gt;</p>
8894````````````````````````````````
8895
8896
8897```````````````````````````````` example
8898< http://foo.bar >
8899.
8900<p>&lt; http://foo.bar &gt;</p>
8901````````````````````````````````
8902
8903
8904```````````````````````````````` example
8905<m:abc>
8906.
8907<p>&lt;m:abc&gt;</p>
8908````````````````````````````````
8909
8910
8911```````````````````````````````` example
8912<foo.bar.baz>
8913.
8914<p>&lt;foo.bar.baz&gt;</p>
8915````````````````````````````````
8916
8917
8918```````````````````````````````` example
8919http://example.com
8920.
8921<p>http://example.com</p>
8922````````````````````````````````
8923
8924
8925```````````````````````````````` example
8926foo@bar.example.com
8927.
8928<p>foo@bar.example.com</p>
8929````````````````````````````````
8930
8931
8932## Raw HTML
8933
8934Text between `<` and `>` that looks like an HTML tag is parsed as a
8935raw HTML tag and will be rendered in HTML without escaping.
8936Tag and attribute names are not limited to current HTML tags,
8937so custom tags (and even, say, DocBook tags) may be used.
8938
8939Here is the grammar for tags:
8940
8941A [tag name](@) consists of an ASCII letter
8942followed by zero or more ASCII letters, digits, or
8943hyphens (`-`).
8944
8945An [attribute](@) consists of spaces, tabs, and up to one line ending,
8946an [attribute name], and an optional
8947[attribute value specification].
8948
8949An [attribute name](@)
8950consists of an ASCII letter, `_`, or `:`, followed by zero or more ASCII
8951letters, digits, `_`, `.`, `:`, or `-`. (Note: This is the XML
8952specification restricted to ASCII. HTML5 is laxer.)
8953
8954An [attribute value specification](@)
8955consists of optional spaces, tabs, and up to one line ending,
8956a `=` character, optional spaces, tabs, and up to one line ending,
8957and an [attribute value].
8958
8959An [attribute value](@)
8960consists of an [unquoted attribute value],
8961a [single-quoted attribute value], or a [double-quoted attribute value].
8962
8963An [unquoted attribute value](@)
8964is a nonempty string of characters not
8965including spaces, tabs, line endings, `"`, `'`, `=`, `<`, `>`, or `` ` ``.
8966
8967A [single-quoted attribute value](@)
8968consists of `'`, zero or more
8969characters not including `'`, and a final `'`.
8970
8971A [double-quoted attribute value](@)
8972consists of `"`, zero or more
8973characters not including `"`, and a final `"`.
8974
8975An [open tag](@) consists of a `<` character, a [tag name],
8976zero or more [attributes], optional spaces, tabs, and up to one line ending,
8977an optional `/` character, and a `>` character.
8978
8979A [closing tag](@) consists of the string `</`, a
8980[tag name], optional spaces, tabs, and up to one line ending, and the character
8981`>`.
8982
8983An [HTML comment](@) consists of `<!--` + *text* + `-->`,
8984where *text* does not start with `>` or `->`, does not end with `-`,
8985and does not contain `--`. (See the
8986[HTML5 spec](http://www.w3.org/TR/html5/syntax.html#comments).)
8987
8988A [processing instruction](@)
8989consists of the string `<?`, a string
8990of characters not including the string `?>`, and the string
8991`?>`.
8992
8993A [declaration](@) consists of the string `<!`, an ASCII letter, zero or more
8994characters not including the character `>`, and the character `>`.
8995
8996A [CDATA section](@) consists of
8997the string `<![CDATA[`, a string of characters not including the string
8998`]]>`, and the string `]]>`.
8999
9000An [HTML tag](@) consists of an [open tag], a [closing tag],
9001an [HTML comment], a [processing instruction], a [declaration],
9002or a [CDATA section].
9003
9004Here are some simple open tags:
9005
9006```````````````````````````````` example
9007<a><bab><c2c>
9008.
9009<p><a><bab><c2c></p>
9010````````````````````````````````
9011
9012
9013Empty elements:
9014
9015```````````````````````````````` example
9016<a/><b2/>
9017.
9018<p><a/><b2/></p>
9019````````````````````````````````
9020
9021
9022Whitespace is allowed:
9023
9024```````````````````````````````` example
9025<a /><b2
9026data="foo" >
9027.
9028<p><a /><b2
9029data="foo" ></p>
9030````````````````````````````````
9031
9032
9033With attributes:
9034
9035```````````````````````````````` example
9036<a foo="bar" bam = 'baz <em>"</em>'
9037_boolean zoop:33=zoop:33 />
9038.
9039<p><a foo="bar" bam = 'baz <em>"</em>'
9040_boolean zoop:33=zoop:33 /></p>
9041````````````````````````````````
9042
9043
9044Custom tag names can be used:
9045
9046```````````````````````````````` example
9047Foo <responsive-image src="foo.jpg" />
9048.
9049<p>Foo <responsive-image src="foo.jpg" /></p>
9050````````````````````````````````
9051
9052
9053Illegal tag names, not parsed as HTML:
9054
9055```````````````````````````````` example
9056<33> <__>
9057.
9058<p>&lt;33&gt; &lt;__&gt;</p>
9059````````````````````````````````
9060
9061
9062Illegal attribute names:
9063
9064```````````````````````````````` example
9065<a h*#ref="hi">
9066.
9067<p>&lt;a h*#ref=&quot;hi&quot;&gt;</p>
9068````````````````````````````````
9069
9070
9071Illegal attribute values:
9072
9073```````````````````````````````` example
9074<a href="hi'> <a href=hi'>
9075.
9076<p>&lt;a href=&quot;hi'&gt; &lt;a href=hi'&gt;</p>
9077````````````````````````````````
9078
9079
9080Illegal whitespace:
9081
9082```````````````````````````````` example
9083< a><
9084foo><bar/ >
9085<foo bar=baz
9086bim!bop />
9087.
9088<p>&lt; a&gt;&lt;
9089foo&gt;&lt;bar/ &gt;
9090&lt;foo bar=baz
9091bim!bop /&gt;</p>
9092````````````````````````````````
9093
9094
9095Missing whitespace:
9096
9097```````````````````````````````` example
9098<a href='bar'title=title>
9099.
9100<p>&lt;a href='bar'title=title&gt;</p>
9101````````````````````````````````
9102
9103
9104Closing tags:
9105
9106```````````````````````````````` example
9107</a></foo >
9108.
9109<p></a></foo ></p>
9110````````````````````````````````
9111
9112
9113Illegal attributes in closing tag:
9114
9115```````````````````````````````` example
9116</a href="foo">
9117.
9118<p>&lt;/a href=&quot;foo&quot;&gt;</p>
9119````````````````````````````````
9120
9121
9122Comments:
9123
9124```````````````````````````````` example
9125foo <!-- this is a
9126comment - with hyphen -->
9127.
9128<p>foo <!-- this is a
9129comment - with hyphen --></p>
9130````````````````````````````````
9131
9132
9133```````````````````````````````` example
9134foo <!-- not a comment -- two hyphens -->
9135.
9136<p>foo &lt;!-- not a comment -- two hyphens --&gt;</p>
9137````````````````````````````````
9138
9139
9140Not comments:
9141
9142```````````````````````````````` example
9143foo <!--> foo -->
9144
9145foo <!-- foo--->
9146.
9147<p>foo &lt;!--&gt; foo --&gt;</p>
9148<p>foo &lt;!-- foo---&gt;</p>
9149````````````````````````````````
9150
9151
9152Processing instructions:
9153
9154```````````````````````````````` example
9155foo <?php echo $a; ?>
9156.
9157<p>foo <?php echo $a; ?></p>
9158````````````````````````````````
9159
9160
9161Declarations:
9162
9163```````````````````````````````` example
9164foo <!ELEMENT br EMPTY>
9165.
9166<p>foo <!ELEMENT br EMPTY></p>
9167````````````````````````````````
9168
9169
9170CDATA sections:
9171
9172```````````````````````````````` example
9173foo <![CDATA[>&<]]>
9174.
9175<p>foo <![CDATA[>&<]]></p>
9176````````````````````````````````
9177
9178
9179Entity and numeric character references are preserved in HTML
9180attributes:
9181
9182```````````````````````````````` example
9183foo <a href="&ouml;">
9184.
9185<p>foo <a href="&ouml;"></p>
9186````````````````````````````````
9187
9188
9189Backslash escapes do not work in HTML attributes:
9190
9191```````````````````````````````` example
9192foo <a href="\*">
9193.
9194<p>foo <a href="\*"></p>
9195````````````````````````````````
9196
9197
9198```````````````````````````````` example
9199<a href="\"">
9200.
9201<p>&lt;a href=&quot;&quot;&quot;&gt;</p>
9202````````````````````````````````
9203
9204
9205## Hard line breaks
9206
9207A line ending (not in a code span or HTML tag) that is preceded
9208by two or more spaces and does not occur at the end of a block
9209is parsed as a [hard line break](@) (rendered
9210in HTML as a `<br />` tag):
9211
9212```````````````````````````````` example
9213foo
9214baz
9215.
9216<p>foo<br />
9217baz</p>
9218````````````````````````````````
9219
9220
9221For a more visible alternative, a backslash before the
9222[line ending] may be used instead of two or more spaces:
9223
9224```````````````````````````````` example
9225foo\
9226baz
9227.
9228<p>foo<br />
9229baz</p>
9230````````````````````````````````
9231
9232
9233More than two spaces can be used:
9234
9235```````````````````````````````` example
9236foo
9237baz
9238.
9239<p>foo<br />
9240baz</p>
9241````````````````````````````````
9242
9243
9244Leading spaces at the beginning of the next line are ignored:
9245
9246```````````````````````````````` example
9247foo
9248 bar
9249.
9250<p>foo<br />
9251bar</p>
9252````````````````````````````````
9253
9254
9255```````````````````````````````` example
9256foo\
9257 bar
9258.
9259<p>foo<br />
9260bar</p>
9261````````````````````````````````
9262
9263
9264Hard line breaks can occur inside emphasis, links, and other constructs
9265that allow inline content:
9266
9267```````````````````````````````` example
9268*foo
9269bar*
9270.
9271<p><em>foo<br />
9272bar</em></p>
9273````````````````````````````````
9274
9275
9276```````````````````````````````` example
9277*foo\
9278bar*
9279.
9280<p><em>foo<br />
9281bar</em></p>
9282````````````````````````````````
9283
9284
9285Hard line breaks do not occur inside code spans
9286
9287```````````````````````````````` example
9288`code
9289span`
9290.
9291<p><code>code span</code></p>
9292````````````````````````````````
9293
9294
9295```````````````````````````````` example
9296`code\
9297span`
9298.
9299<p><code>code\ span</code></p>
9300````````````````````````````````
9301
9302
9303or HTML tags:
9304
9305```````````````````````````````` example
9306<a href="foo
9307bar">
9308.
9309<p><a href="foo
9310bar"></p>
9311````````````````````````````````
9312
9313
9314```````````````````````````````` example
9315<a href="foo\
9316bar">
9317.
9318<p><a href="foo\
9319bar"></p>
9320````````````````````````````````
9321
9322
9323Hard line breaks are for separating inline content within a block.
9324Neither syntax for hard line breaks works at the end of a paragraph or
9325other block element:
9326
9327```````````````````````````````` example
9328foo\
9329.
9330<p>foo\</p>
9331````````````````````````````````
9332
9333
9334```````````````````````````````` example
9335foo
9336.
9337<p>foo</p>
9338````````````````````````````````
9339
9340
9341```````````````````````````````` example
9342### foo\
9343.
9344<h3>foo\</h3>
9345````````````````````````````````
9346
9347
9348```````````````````````````````` example
9349### foo
9350.
9351<h3>foo</h3>
9352````````````````````````````````
9353
9354
9355## Soft line breaks
9356
9357A regular line ending (not in a code span or HTML tag) that is not
9358preceded by two or more spaces or a backslash is parsed as a
9359[softbreak](@). (A soft line break may be rendered in HTML either as a
9360[line ending] or as a space. The result will be the same in
9361browsers. In the examples here, a [line ending] will be used.)
9362
9363```````````````````````````````` example
9364foo
9365baz
9366.
9367<p>foo
9368baz</p>
9369````````````````````````````````
9370
9371
9372Spaces at the end of the line and beginning of the next line are
9373removed:
9374
9375```````````````````````````````` example
9376foo
9377 baz
9378.
9379<p>foo
9380baz</p>
9381````````````````````````````````
9382
9383
9384A conforming parser may render a soft line break in HTML either as a
9385line ending or as a space.
9386
9387A renderer may also provide an option to render soft line breaks
9388as hard line breaks.
9389
9390## Textual content
9391
9392Any characters not given an interpretation by the above rules will
9393be parsed as plain textual content.
9394
9395```````````````````````````````` example
9396hello $.;'there
9397.
9398<p>hello $.;'there</p>
9399````````````````````````````````
9400
9401
9402```````````````````````````````` example
9403Foo χρῆν
9404.
9405<p>Foo χρῆν</p>
9406````````````````````````````````
9407
9408
9409Internal spaces are preserved verbatim:
9410
9411```````````````````````````````` example
9412Multiple spaces
9413.
9414<p>Multiple spaces</p>
9415````````````````````````````````
9416
9417
9418<!-- END TESTS -->
9419
9420# Appendix: A parsing strategy
9421
9422In this appendix we describe some features of the parsing strategy
9423used in the CommonMark reference implementations.
9424
9425## Overview
9426
9427Parsing has two phases:
9428
94291. In the first phase, lines of input are consumed and the block
9430structure of the document---its division into paragraphs, block quotes,
9431list items, and so on---is constructed. Text is assigned to these
9432blocks but not parsed. Link reference definitions are parsed and a
9433map of links is constructed.
9434
94352. In the second phase, the raw text contents of paragraphs and headings
9436are parsed into sequences of Markdown inline elements (strings,
9437code spans, links, emphasis, and so on), using the map of link
9438references constructed in phase 1.
9439
9440At each point in processing, the document is represented as a tree of
9441**blocks**. The root of the tree is a `document` block. The `document`
9442may have any number of other blocks as **children**. These children
9443may, in turn, have other blocks as children. The last child of a block
9444is normally considered **open**, meaning that subsequent lines of input
9445can alter its contents. (Blocks that are not open are **closed**.)
9446Here, for example, is a possible document tree, with the open blocks
9447marked by arrows:
9448
9449``` tree
9450-> document
9451 -> block_quote
9452 paragraph
9453 "Lorem ipsum dolor\nsit amet."
9454 -> list (type=bullet tight=true bullet_char=-)
9455 list_item
9456 paragraph
9457 "Qui *quodsi iracundia*"
9458 -> list_item
9459 -> paragraph
9460 "aliquando id"
9461```
9462
9463## Phase 1: block structure
9464
9465Each line that is processed has an effect on this tree. The line is
9466analyzed and, depending on its contents, the document may be altered
9467in one or more of the following ways:
9468
94691. One or more open blocks may be closed.
94702. One or more new blocks may be created as children of the
9471 last open block.
94723. Text may be added to the last (deepest) open block remaining
9473 on the tree.
9474
9475Once a line has been incorporated into the tree in this way,
9476it can be discarded, so input can be read in a stream.
9477
9478For each line, we follow this procedure:
9479
94801. First we iterate through the open blocks, starting with the
9481root document, and descending through last children down to the last
9482open block. Each block imposes a condition that the line must satisfy
9483if the block is to remain open. For example, a block quote requires a
9484`>` character. A paragraph requires a non-blank line.
9485In this phase we may match all or just some of the open
9486blocks. But we cannot close unmatched blocks yet, because we may have a
9487[lazy continuation line].
9488
94892. Next, after consuming the continuation markers for existing
9490blocks, we look for new block starts (e.g. `>` for a block quote).
9491If we encounter a new block start, we close any blocks unmatched
9492in step 1 before creating the new block as a child of the last
9493matched container block.
9494
94953. Finally, we look at the remainder of the line (after block
9496markers like `>`, list markers, and indentation have been consumed).
9497This is text that can be incorporated into the last open
9498block (a paragraph, code block, heading, or raw HTML).
9499
9500Setext headings are formed when we see a line of a paragraph
9501that is a [setext heading underline].
9502
9503Reference link definitions are detected when a paragraph is closed;
9504the accumulated text lines are parsed to see if they begin with
9505one or more reference link definitions. Any remainder becomes a
9506normal paragraph.
9507
9508We can see how this works by considering how the tree above is
9509generated by four lines of Markdown:
9510
9511``` markdown
9512> Lorem ipsum dolor
9513sit amet.
9514> - Qui *quodsi iracundia*
9515> - aliquando id
9516```
9517
9518At the outset, our document model is just
9519
9520``` tree
9521-> document
9522```
9523
9524The first line of our text,
9525
9526``` markdown
9527> Lorem ipsum dolor
9528```
9529
9530causes a `block_quote` block to be created as a child of our
9531open `document` block, and a `paragraph` block as a child of
9532the `block_quote`. Then the text is added to the last open
9533block, the `paragraph`:
9534
9535``` tree
9536-> document
9537 -> block_quote
9538 -> paragraph
9539 "Lorem ipsum dolor"
9540```
9541
9542The next line,
9543
9544``` markdown
9545sit amet.
9546```
9547
9548is a "lazy continuation" of the open `paragraph`, so it gets added
9549to the paragraph's text:
9550
9551``` tree
9552-> document
9553 -> block_quote
9554 -> paragraph
9555 "Lorem ipsum dolor\nsit amet."
9556```
9557
9558The third line,
9559
9560``` markdown
9561> - Qui *quodsi iracundia*
9562```
9563
9564causes the `paragraph` block to be closed, and a new `list` block
9565opened as a child of the `block_quote`. A `list_item` is also
9566added as a child of the `list`, and a `paragraph` as a child of
9567the `list_item`. The text is then added to the new `paragraph`:
9568
9569``` tree
9570-> document
9571 -> block_quote
9572 paragraph
9573 "Lorem ipsum dolor\nsit amet."
9574 -> list (type=bullet tight=true bullet_char=-)
9575 -> list_item
9576 -> paragraph
9577 "Qui *quodsi iracundia*"
9578```
9579
9580The fourth line,
9581
9582``` markdown
9583> - aliquando id
9584```
9585
9586causes the `list_item` (and its child the `paragraph`) to be closed,
9587and a new `list_item` opened up as child of the `list`. A `paragraph`
9588is added as a child of the new `list_item`, to contain the text.
9589We thus obtain the final tree:
9590
9591``` tree
9592-> document
9593 -> block_quote
9594 paragraph
9595 "Lorem ipsum dolor\nsit amet."
9596 -> list (type=bullet tight=true bullet_char=-)
9597 list_item
9598 paragraph
9599 "Qui *quodsi iracundia*"
9600 -> list_item
9601 -> paragraph
9602 "aliquando id"
9603```
9604
9605## Phase 2: inline structure
9606
9607Once all of the input has been parsed, all open blocks are closed.
9608
9609We then "walk the tree," visiting every node, and parse raw
9610string contents of paragraphs and headings as inlines. At this
9611point we have seen all the link reference definitions, so we can
9612resolve reference links as we go.
9613
9614``` tree
9615document
9616 block_quote
9617 paragraph
9618 str "Lorem ipsum dolor"
9619 softbreak
9620 str "sit amet."
9621 list (type=bullet tight=true bullet_char=-)
9622 list_item
9623 paragraph
9624 str "Qui "
9625 emph
9626 str "quodsi iracundia"
9627 list_item
9628 paragraph
9629 str "aliquando id"
9630```
9631
9632Notice how the [line ending] in the first paragraph has
9633been parsed as a `softbreak`, and the asterisks in the first list item
9634have become an `emph`.
9635
9636### An algorithm for parsing nested emphasis and links
9637
9638By far the trickiest part of inline parsing is handling emphasis,
9639strong emphasis, links, and images. This is done using the following
9640algorithm.
9641
9642When we're parsing inlines and we hit either
9643
9644- a run of `*` or `_` characters, or
9645- a `[` or `![`
9646
9647we insert a text node with these symbols as its literal content, and we
9648add a pointer to this text node to the [delimiter stack](@).
9649
9650The [delimiter stack] is a doubly linked list. Each
9651element contains a pointer to a text node, plus information about
9652
9653- the type of delimiter (`[`, `![`, `*`, `_`)
9654- the number of delimiters,
9655- whether the delimiter is "active" (all are active to start), and
9656- whether the delimiter is a potential opener, a potential closer,
9657 or both (which depends on what sort of characters precede
9658 and follow the delimiters).
9659
9660When we hit a `]` character, we call the *look for link or image*
9661procedure (see below).
9662
9663When we hit the end of the input, we call the *process emphasis*
9664procedure (see below), with `stack_bottom` = NULL.
9665
9666#### *look for link or image*
9667
9668Starting at the top of the delimiter stack, we look backwards
9669through the stack for an opening `[` or `![` delimiter.
9670
9671- If we don't find one, we return a literal text node `]`.
9672
9673- If we do find one, but it's not *active*, we remove the inactive
9674 delimiter from the stack, and return a literal text node `]`.
9675
9676- If we find one and it's active, then we parse ahead to see if
9677 we have an inline link/image, reference link/image, compact reference
9678 link/image, or shortcut reference link/image.
9679
9680 + If we don't, then we remove the opening delimiter from the
9681 delimiter stack and return a literal text node `]`.
9682
9683 + If we do, then
9684
9685 * We return a link or image node whose children are the inlines
9686 after the text node pointed to by the opening delimiter.
9687
9688 * We run *process emphasis* on these inlines, with the `[` opener
9689 as `stack_bottom`.
9690
9691 * We remove the opening delimiter.
9692
9693 * If we have a link (and not an image), we also set all
9694 `[` delimiters before the opening delimiter to *inactive*. (This
9695 will prevent us from getting links within links.)
9696
9697#### *process emphasis*
9698
9699Parameter `stack_bottom` sets a lower bound to how far we
9700descend in the [delimiter stack]. If it is NULL, we can
9701go all the way to the bottom. Otherwise, we stop before
9702visiting `stack_bottom`.
9703
9704Let `current_position` point to the element on the [delimiter stack]
9705just above `stack_bottom` (or the first element if `stack_bottom`
9706is NULL).
9707
9708We keep track of the `openers_bottom` for each delimiter
9709type (`*`, `_`), indexed to the length of the closing delimiter run
9710(modulo 3) and to whether the closing delimiter can also be an
9711opener. Initialize this to `stack_bottom`.
9712
9713Then we repeat the following until we run out of potential
9714closers:
9715
9716- Move `current_position` forward in the delimiter stack (if needed)
9717 until we find the first potential closer with delimiter `*` or `_`.
9718 (This will be the potential closer closest
9719 to the beginning of the input -- the first one in parse order.)
9720
9721- Now, look back in the stack (staying above `stack_bottom` and
9722 the `openers_bottom` for this delimiter type) for the
9723 first matching potential opener ("matching" means same delimiter).
9724
9725- If one is found:
9726
9727 + Figure out whether we have emphasis or strong emphasis:
9728 if both closer and opener spans have length >= 2, we have
9729 strong, otherwise regular.
9730
9731 + Insert an emph or strong emph node accordingly, after
9732 the text node corresponding to the opener.
9733
9734 + Remove any delimiters between the opener and closer from
9735 the delimiter stack.
9736
9737 + Remove 1 (for regular emph) or 2 (for strong emph) delimiters
9738 from the opening and closing text nodes. If they become empty
9739 as a result, remove them and remove the corresponding element
9740 of the delimiter stack. If the closing node is removed, reset
9741 `current_position` to the next element in the stack.
9742
9743- If none is found:
9744
9745 + Set `openers_bottom` to the element before `current_position`.
9746 (We know that there are no openers for this kind of closer up to and
9747 including this point, so this puts a lower bound on future searches.)
9748
9749 + If the closer at `current_position` is not a potential opener,
9750 remove it from the delimiter stack (since we know it can't
9751 be a closer either).
9752
9753 + Advance `current_position` to the next element in the stack.
9754
9755After we're done, we remove all delimiters above `stack_bottom` from the
9756delimiter stack.
crates/markdown-it/tests/fixtures/deno.lock created+72
...@@ -0,0 +1,72 @@
1{
2 "version": "3",
3 "packages": {
4 "specifiers": {
5 "npm:markdown-it-testgen": "npm:markdown-it-testgen@0.1.6"
6 },
7 "npm": {
8 "argparse@1.0.10": {
9 "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
10 "dependencies": {
11 "sprintf-js": "sprintf-js@1.0.3"
12 }
13 },
14 "assertion-error@1.0.0": {
15 "integrity": "sha512-g/gZV+G476cnmtYI+Ko9d5khxSoCSoom/EaNmmCfwpOvBXEJ18qwFrxfP1/CsIqk2no1sAKKwxndV0tP7ROOFQ==",
16 "dependencies": {}
17 },
18 "chai@1.10.0": {
19 "integrity": "sha512-E3L9M2SeQU1XagJkE9KJyTAXXHKJkJ1EsKkFp0Rl53lYa3mro2PVgYHNiCb2YRa2nUeyg7aqmI1EIcSBayNd5w==",
20 "dependencies": {
21 "assertion-error": "assertion-error@1.0.0",
22 "deep-eql": "deep-eql@0.1.3"
23 }
24 },
25 "deep-eql@0.1.3": {
26 "integrity": "sha512-6sEotTRGBFiNcqVoeHwnfopbSpi5NbH1VWJmYCVkmxMmaVTT0bUTrNaGyBwhgP4MZL012W/mkzIn3Da+iDYweg==",
27 "dependencies": {
28 "type-detect": "type-detect@0.1.1"
29 }
30 },
31 "esprima@4.0.1": {
32 "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
33 "dependencies": {}
34 },
35 "js-yaml@3.14.1": {
36 "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
37 "dependencies": {
38 "argparse": "argparse@1.0.10",
39 "esprima": "esprima@4.0.1"
40 }
41 },
42 "markdown-it-testgen@0.1.6": {
43 "integrity": "sha512-hYC71G4Mcv3Y7fLTsi4PyoHKSs0P4UgkpzmGBtUYoR/TS83lFbfXUMaI71OiMJ9r4p3fbMhHBwdNTLhSDwmt6Q==",
44 "dependencies": {
45 "chai": "chai@1.10.0",
46 "js-yaml": "js-yaml@3.14.1",
47 "object-assign": "object-assign@4.1.1"
48 }
49 },
50 "object-assign@4.1.1": {
51 "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
52 "dependencies": {}
53 },
54 "sprintf-js@1.0.3": {
55 "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
56 "dependencies": {}
57 },
58 "type-detect@0.1.1": {
59 "integrity": "sha512-5rqszGVwYgBoDkIm2oUtvkfZMQ0vk29iDMU0W2qCa3rG0vPDNczCMT4hV/bLBgLg8k8ri6+u3Zbt+S/14eMzlA==",
60 "dependencies": {}
61 }
62 }
63 },
64 "remote": {},
65 "workspace": {
66 "packageJson": {
67 "dependencies": [
68 "npm:markdown-it-testgen@^0.1.6"
69 ]
70 }
71 }
72}
crates/markdown-it/tests/fixtures/markdown-it/markdown-it-rs.txt created+6
...@@ -0,0 +1,6 @@
1regression test for panic in #40
2.
3![hello'"`world](x)\
4.
5<p><img src="x" alt="hello'&quot;`world">\</p>
6.
crates/markdown-it/tests/fixtures/markdown-it/smartquotes.txt created+192
...@@ -0,0 +1,192 @@
1Should parse nested quotes:
2.
3"foo 'bar' baz"
4
5'foo 'bar' baz'
6.
7<p>“foo ‘bar’ baz”</p>
8<p>‘foo ‘bar’ baz’</p>
9.
10
11
12Should not overlap quotes:
13.
14'foo "bar' baz"
15.
16<p>‘foo &quot;bar’ baz&quot;</p>
17.
18
19
20Should match quotes on the same level:
21.
22"foo *bar* baz"
23.
24<p>“foo <em>bar</em> baz”</p>
25.
26
27
28Should handle adjacent nested quotes:
29.
30'"double in single"'
31
32"'single in double'"
33.
34<p>‘“double in single”’</p>
35<p>“‘single in double’”</p>
36.
37
38
39
40Should not match quotes on different levels:
41.
42*"foo* bar"
43
44"foo *bar"*
45
46*"foo* bar *baz"*
47.
48<p><em>&quot;foo</em> bar&quot;</p>
49<p>&quot;foo <em>bar&quot;</em></p>
50<p><em>&quot;foo</em> bar <em>baz&quot;</em></p>
51.
52
53Smartquotes should not overlap with other tags:
54.
55*foo "bar* *baz" quux*
56.
57<p><em>foo &quot;bar</em> <em>baz&quot; quux</em></p>
58.
59
60
61Should try and find matching quote in this case:
62.
63"foo "bar 'baz"
64.
65<p>&quot;foo “bar 'baz”</p>
66.
67
68
69Should not touch 'inches' in quotes:
70.
71"Monitor 21"" and "Monitor""
72.
73<p>“Monitor 21&quot;” and “Monitor”&quot;</p>
74.
75
76
77Should render an apostrophe as a rsquo:
78.
79This isn't and can't be the best approach to implement this...
80.
81<p>This isn’t and can’t be the best approach to implement this…</p>
82.
83
84
85Apostrophe could end the word, that's why original smartypants replaces all of them as rsquo:
86.
87users' stuff
88.
89<p>users’ stuff</p>
90.
91
92Quotes between punctuation chars:
93
94.
95"(hai)".
96.
97<p>“(hai)”.</p>
98.
99
100Quotes at the start/end of the tokens:
101.
102"*foo* bar"
103
104"foo *bar*"
105
106"*foo bar*"
107.
108<p>“<em>foo</em> bar”</p>
109<p>“foo <em>bar</em>”</p>
110<p>“<em>foo bar</em>”</p>
111.
112
113Should treat softbreak as a space:
114.
115"this"
116and "that".
117
118"this" and
119"that".
120.
121<p>“this”
122and “that”.</p>
123<p>“this” and
124“that”.</p>
125.
126
127Should treat hardbreak as a space:
128.
129"this"\
130and "that".
131
132"this" and\
133"that".
134.
135<p>“this”<br>
136and “that”.</p>
137<p>“this” and<br>
138“that”.</p>
139.
140
141Should allow quotes adjacent to other punctuation characters, #643:
142.
143The dog---"'man's' best friend"
144.
145<p>The dog—“‘man’s’ best friend”</p>
146.
147
148Should parse quotes adjacent to code block, #677:
149.
150"test `code`"
151
152"`code` test"
153.
154<p>“test <code>code</code>”</p>
155<p>“<code>code</code> test”</p>
156.
157
158Should parse quotes adjacent to inline html, #677:
159.
160"test <br>"
161
162"<br> test"
163.
164<p>“test <br>”</p>
165<p>“<br> test”</p>
166.
167
168Should be escapable:
169.
170"foo"
171
172\"foo"
173
174"foo\"
175.
176<p>“foo”</p>
177<p>&quot;foo&quot;</p>
178<p>&quot;foo&quot;</p>
179.
180
181Should not replace entities:
182.
183&quot;foo&quot;
184
185&quot;foo"
186
187"foo&quot;
188.
189<p>&quot;foo&quot;</p>
190<p>&quot;foo&quot;</p>
191<p>&quot;foo&quot;</p>
192.
crates/markdown-it/tests/fixtures/markdown-it/tables.txt created+808
...@@ -0,0 +1,808 @@
1Simple:
2.
3| Heading 1 | Heading 2
4| --------- | ---------
5| Cell 1 | Cell 2
6| Cell 3 | Cell 4
7.
8<table>
9<thead>
10<tr>
11<th>Heading 1</th>
12<th>Heading 2</th>
13</tr>
14</thead>
15<tbody>
16<tr>
17<td>Cell 1</td>
18<td>Cell 2</td>
19</tr>
20<tr>
21<td>Cell 3</td>
22<td>Cell 4</td>
23</tr>
24</tbody>
25</table>
26.
27
28
29Column alignment:
30.
31| Header 1 | Header 2 | Header 3 | Header 4 |
32| :------: | -------: | :------- | -------- |
33| Cell 1 | Cell 2 | Cell 3 | Cell 4 |
34| Cell 5 | Cell 6 | Cell 7 | Cell 8 |
35.
36<table>
37<thead>
38<tr>
39<th style="text-align:center">Header 1</th>
40<th style="text-align:right">Header 2</th>
41<th style="text-align:left">Header 3</th>
42<th>Header 4</th>
43</tr>
44</thead>
45<tbody>
46<tr>
47<td style="text-align:center">Cell 1</td>
48<td style="text-align:right">Cell 2</td>
49<td style="text-align:left">Cell 3</td>
50<td>Cell 4</td>
51</tr>
52<tr>
53<td style="text-align:center">Cell 5</td>
54<td style="text-align:right">Cell 6</td>
55<td style="text-align:left">Cell 7</td>
56<td>Cell 8</td>
57</tr>
58</tbody>
59</table>
60.
61
62
63Nested emphases:
64.
65Header 1|Header 2|Header 3|Header 4
66:-------|:------:|-------:|--------
67Cell 1 |Cell 2 |Cell 3 |Cell 4
68*Cell 5*|Cell 6 |Cell 7 |Cell 8
69.
70<table>
71<thead>
72<tr>
73<th style="text-align:left">Header 1</th>
74<th style="text-align:center">Header 2</th>
75<th style="text-align:right">Header 3</th>
76<th>Header 4</th>
77</tr>
78</thead>
79<tbody>
80<tr>
81<td style="text-align:left">Cell 1</td>
82<td style="text-align:center">Cell 2</td>
83<td style="text-align:right">Cell 3</td>
84<td>Cell 4</td>
85</tr>
86<tr>
87<td style="text-align:left"><em>Cell 5</em></td>
88<td style="text-align:center">Cell 6</td>
89<td style="text-align:right">Cell 7</td>
90<td>Cell 8</td>
91</tr>
92</tbody>
93</table>
94.
95
96
97Nested tables inside blockquotes:
98.
99> foo|foo
100> ---|---
101> bar|bar
102baz|baz
103.
104<blockquote>
105<table>
106<thead>
107<tr>
108<th>foo</th>
109<th>foo</th>
110</tr>
111</thead>
112<tbody>
113<tr>
114<td>bar</td>
115<td>bar</td>
116</tr>
117</tbody>
118</table>
119</blockquote>
120<p>baz|baz</p>
121.
122
123
124Minimal one-column:
125.
126| foo
127|----
128| test2
129.
130<table>
131<thead>
132<tr>
133<th>foo</th>
134</tr>
135</thead>
136<tbody>
137<tr>
138<td>test2</td>
139</tr>
140</tbody>
141</table>
142.
143
144
145This is parsed as one big table:
146.
147- foo|foo
148---|---
149bar|bar
150.
151<table>
152<thead>
153<tr>
154<th>- foo</th>
155<th>foo</th>
156</tr>
157</thead>
158<tbody>
159<tr>
160<td>bar</td>
161<td>bar</td>
162</tr>
163</tbody>
164</table>
165.
166
167
168Second line should not contain symbols except "-", ":", "|" and " ":
169.
170foo|foo
171-----|-----s
172bar|bar
173.
174<p>foo|foo
175-----|-----s
176bar|bar</p>
177.
178
179
180Second line should contain "|" symbol:
181.
182foo|foo
183-----:-----
184bar|bar
185.
186<p>foo|foo
187-----:-----
188bar|bar</p>
189.
190
191
192Second line should not have empty columns in the middle:
193.
194foo|foo
195-----||-----
196bar|bar
197.
198<p>foo|foo
199-----||-----
200bar|bar</p>
201.
202
203
204Wrong alignment symbol position:
205.
206foo|foo
207-----|-::-
208bar|bar
209.
210<p>foo|foo
211-----|-::-
212bar|bar</p>
213.
214
215
216Title line should contain "|" symbol:
217.
218foo
219-----|-----
220bar|bar
221.
222<p>foo
223-----|-----
224bar|bar</p>
225.
226
227
228Allow tabs as a separator on 2nd line
229.
230| foo | bar |
231| --- | --- |
232| baz | quux |
233.
234<table>
235<thead>
236<tr>
237<th>foo</th>
238<th>bar</th>
239</tr>
240</thead>
241<tbody>
242<tr>
243<td>baz</td>
244<td>quux</td>
245</tr>
246</tbody>
247</table>
248.
249
250
251Should terminate paragraph:
252.
253paragraph
254foo|foo
255---|---
256bar|bar
257.
258<p>paragraph</p>
259<table>
260<thead>
261<tr>
262<th>foo</th>
263<th>foo</th>
264</tr>
265</thead>
266<tbody>
267<tr>
268<td>bar</td>
269<td>bar</td>
270</tr>
271</tbody>
272</table>
273.
274
275
276Another complicated backticks case
277.
278| Heading 1 | Heading 2
279| --------- | ---------
280| Cell 1 | Cell 2
281| \\\`|\\\`
282.
283<table>
284<thead>
285<tr>
286<th>Heading 1</th>
287<th>Heading 2</th>
288</tr>
289</thead>
290<tbody>
291<tr>
292<td>Cell 1</td>
293<td>Cell 2</td>
294</tr>
295<tr>
296<td>\`</td>
297<td>\`</td>
298</tr>
299</tbody>
300</table>
301.
302
303`\` in tables should not count as escaped backtick
304.
305# | 1 | 2
306--|--|--
307x | `\` | `x`
308.
309<table>
310<thead>
311<tr>
312<th>#</th>
313<th>1</th>
314<th>2</th>
315</tr>
316</thead>
317<tbody>
318<tr>
319<td>x</td>
320<td><code>\</code></td>
321<td><code>x</code></td>
322</tr>
323</tbody>
324</table>
325.
326
327Tables should handle escaped backticks
328.
329# | 1 | 2
330--|--|--
331x | \`\` | `x`
332.
333<table>
334<thead>
335<tr>
336<th>#</th>
337<th>1</th>
338<th>2</th>
339</tr>
340</thead>
341<tbody>
342<tr>
343<td>x</td>
344<td>``</td>
345<td><code>x</code></td>
346</tr>
347</tbody>
348</table>
349.
350
351
352An amount of rows might be different across the table (issue #171):
353.
354| 1 | 2 |
355| :-----: | :-----: |
356| 3 | 4 | 5 | 6 |
357.
358<table>
359<thead>
360<tr>
361<th style="text-align:center">1</th>
362<th style="text-align:center">2</th>
363</tr>
364</thead>
365<tbody>
366<tr>
367<td style="text-align:center">3</td>
368<td style="text-align:center">4</td>
369</tr>
370</tbody>
371</table>
372.
373
374
375An amount of rows might be different across the table #2:
376.
377| 1 | 2 | 3 | 4 |
378| :-----: | :-----: | :-----: | :-----: |
379| 5 | 6 |
380.
381<table>
382<thead>
383<tr>
384<th style="text-align:center">1</th>
385<th style="text-align:center">2</th>
386<th style="text-align:center">3</th>
387<th style="text-align:center">4</th>
388</tr>
389</thead>
390<tbody>
391<tr>
392<td style="text-align:center">5</td>
393<td style="text-align:center">6</td>
394<td style="text-align:center"></td>
395<td style="text-align:center"></td>
396</tr>
397</tbody>
398</table>
399.
400
401
402Allow one-column tables (issue #171):
403.
404| foo |
405:-----:
406| bar |
407.
408<table>
409<thead>
410<tr>
411<th style="text-align:center">foo</th>
412</tr>
413</thead>
414<tbody>
415<tr>
416<td style="text-align:center">bar</td>
417</tr>
418</tbody>
419</table>
420.
421
422
423Allow indented tables (issue #325):
424.
425 | Col1a | Col2a |
426 | ----- | ----- |
427 | Col1b | Col2b |
428.
429<table>
430<thead>
431<tr>
432<th>Col1a</th>
433<th>Col2a</th>
434</tr>
435</thead>
436<tbody>
437<tr>
438<td>Col1b</td>
439<td>Col2b</td>
440</tr>
441</tbody>
442</table>
443.
444
445
446Tables should not be indented more than 4 spaces (1st line):
447.
448 | Col1a | Col2a |
449 | ----- | ----- |
450 | Col1b | Col2b |
451.
452<pre><code>| Col1a | Col2a |
453</code></pre>
454<p>| ----- | ----- |
455| Col1b | Col2b |</p>
456.
457
458
459Tables should not be indented more than 4 spaces (2nd line):
460.
461 | Col1a | Col2a |
462 | ----- | ----- |
463 | Col1b | Col2b |
464.
465<p>| Col1a | Col2a |
466| ----- | ----- |
467| Col1b | Col2b |</p>
468.
469
470
471Tables should not be indented more than 4 spaces (3rd line):
472.
473 | Col1a | Col2a |
474 | ----- | ----- |
475 | Col1b | Col2b |
476.
477<table>
478<thead>
479<tr>
480<th>Col1a</th>
481<th>Col2a</th>
482</tr>
483</thead>
484</table>
485<pre><code>| Col1b | Col2b |
486</code></pre>
487.
488
489
490Allow tables with empty body:
491.
492 | Col1a | Col2a |
493 | ----- | ----- |
494.
495<table>
496<thead>
497<tr>
498<th>Col1a</th>
499<th>Col2a</th>
500</tr>
501</thead>
502</table>
503.
504
505
506Align row should be at least as large as any actual rows:
507.
508Col1a | Col1b | Col1c
509----- | -----
510Col2a | Col2b | Col2c
511.
512<p>Col1a | Col1b | Col1c
513----- | -----
514Col2a | Col2b | Col2c</p>
515.
516
517Escaped pipes inside backticks don't split cells:
518.
519| Heading 1 | Heading 2
520| --------- | ---------
521| Cell 1 | Cell 2
522| `Cell 3\|` | Cell 4
523.
524<table>
525<thead>
526<tr>
527<th>Heading 1</th>
528<th>Heading 2</th>
529</tr>
530</thead>
531<tbody>
532<tr>
533<td>Cell 1</td>
534<td>Cell 2</td>
535</tr>
536<tr>
537<td><code>Cell 3|</code></td>
538<td>Cell 4</td>
539</tr>
540</tbody>
541</table>
542.
543
544Escape before escaped Pipes inside backticks don't split cells:
545.
546| Heading 1 | Heading 2
547| --------- | ---------
548| Cell 1 | Cell 2
549| `Cell 3\\|` | Cell 4
550.
551<table>
552<thead>
553<tr>
554<th>Heading 1</th>
555<th>Heading 2</th>
556</tr>
557</thead>
558<tbody>
559<tr>
560<td>Cell 1</td>
561<td>Cell 2</td>
562</tr>
563<tr>
564<td><code>Cell 3\|</code></td>
565<td>Cell 4</td>
566</tr>
567</tbody>
568</table>
569.
570
571Regression test for #721, table in a list indented with tabs:
572.
573- Level 1
574
575 - Level 2
576
577 | Column 1 | Column 2 |
578 | -------- | -------- |
579 | abcdefgh | ijklmnop |
580.
581<ul>
582<li>
583<p>Level 1</p>
584<ul>
585<li>
586<p>Level 2</p>
587<table>
588<thead>
589<tr>
590<th>Column 1</th>
591<th>Column 2</th>
592</tr>
593</thead>
594<tbody>
595<tr>
596<td>abcdefgh</td>
597<td>ijklmnop</td>
598</tr>
599</tbody>
600</table>
601</li>
602</ul>
603</li>
604</ul>
605.
606
607
608Table without any columns is not a table, #724
609.
610|
611|
612|
613.
614<p>|
615|
616|</p>
617.
618
619
620GFM 4.10 Tables (extension), Example 198
621.
622| foo | bar |
623| --- | --- |
624| baz | bim |
625.
626<table>
627<thead>
628<tr>
629<th>foo</th>
630<th>bar</th>
631</tr>
632</thead>
633<tbody>
634<tr>
635<td>baz</td>
636<td>bim</td>
637</tr>
638</tbody>
639</table>
640.
641
642GFM 4.10 Tables (extension), Example 199
643.
644| abc | defghi |
645:-: | -----------:
646bar | baz
647.
648<table>
649<thead>
650<tr>
651<th style="text-align:center">abc</th>
652<th style="text-align:right">defghi</th>
653</tr>
654</thead>
655<tbody>
656<tr>
657<td style="text-align:center">bar</td>
658<td style="text-align:right">baz</td>
659</tr>
660</tbody>
661</table>
662.
663
664GFM 4.10 Tables (extension), Example 200
665.
666| f\|oo |
667| ------ |
668| b `\|` az |
669| b **\|** im |
670.
671<table>
672<thead>
673<tr>
674<th>f|oo</th>
675</tr>
676</thead>
677<tbody>
678<tr>
679<td>b <code>|</code> az</td>
680</tr>
681<tr>
682<td>b <strong>|</strong> im</td>
683</tr>
684</tbody>
685</table>
686.
687
688GFM 4.10 Tables (extension), Example 201
689.
690| abc | def |
691| --- | --- |
692| bar | baz |
693> bar
694.
695<table>
696<thead>
697<tr>
698<th>abc</th>
699<th>def</th>
700</tr>
701</thead>
702<tbody>
703<tr>
704<td>bar</td>
705<td>baz</td>
706</tr>
707</tbody>
708</table>
709<blockquote>
710<p>bar</p>
711</blockquote>
712.
713
714GFM 4.10 Tables (extension), Example 202
715.
716| abc | def |
717| --- | --- |
718| bar | baz |
719bar
720
721bar
722.
723<table>
724<thead>
725<tr>
726<th>abc</th>
727<th>def</th>
728</tr>
729</thead>
730<tbody>
731<tr>
732<td>bar</td>
733<td>baz</td>
734</tr>
735<tr>
736<td>bar</td>
737<td></td>
738</tr>
739</tbody>
740</table>
741<p>bar</p>
742.
743
744GFM 4.10 Tables (extension), Example 203
745.
746| abc | def |
747| --- |
748| bar |
749.
750<p>| abc | def |
751| — |
752| bar |</p>
753.
754
755GFM 4.10 Tables (extension), Example 204
756.
757| abc | def |
758| --- | --- |
759| bar |
760| bar | baz | boo |
761.
762<table>
763<thead>
764<tr>
765<th>abc</th>
766<th>def</th>
767</tr>
768</thead>
769<tbody>
770<tr>
771<td>bar</td>
772<td></td>
773</tr>
774<tr>
775<td>bar</td>
776<td>baz</td>
777</tr>
778</tbody>
779</table>
780.
781
782GFM 4.10 Tables (extension), Example 205
783.
784| abc | def |
785| --- | --- |
786.
787<table>
788<thead>
789<tr>
790<th>abc</th>
791<th>def</th>
792</tr>
793</thead>
794</table>
795.
796
797A list takes precedence in case of ambiguity
798.
799a | b
800- | -
8011 | 2
802.
803<p>a | b</p>
804<ul>
805<li>| -
8061 | 2</li>
807</ul>
808.
crates/markdown-it/tests/fixtures/markdown-it/typographer-extra.txt created+14
...@@ -0,0 +1,14 @@
1don't touch text in autolinks
2.
3URL with (C) (c) (R) (r) (TM) (tm): https://example.com/(c)(r)(tm)/(C)(R)(TM) what do you think?
4.
5<p>URL with © © ® ® ™ ™: <a href="https://example.com/(c)(r)(tm)/(C)(R)(TM)">https://example.com/(c)(r)(tm)/(C)(R)(TM)</a> what do you think?</p>
6.
7
8
9replacements for TM should allow mixed case tM and Tm
10.
11These two should both end up the same as (TM) and (tm): (tM), (Tm).
12.
13<p>These two should both end up the same as ™ and ™: ™, ™.</p>
14.
crates/markdown-it/tests/fixtures/markdown-it/typographer.txt created+110
...@@ -0,0 +1,110 @@
1.
2(bad)
3.
4<p>(bad)</p>
5.
6
7
8copyright
9.
10(c) (C)
11.
12<p>© ©</p>
13.
14
15
16reserved
17.
18(r) (R)
19.
20<p>® ®</p>
21.
22
23
24trademark
25.
26(tm) (TM)
27.
28<p>™ ™</p>
29.
30
31
32plus-minus
33.
34+-5
35.
36<p>±5</p>
37.
38
39
40ellipsis
41.
42test.. test... test..... test?..... test!....
43.
44<p>test… test… test… test?.. test!..</p>
45.
46
47
48dupes
49.
50!!!!!! ???? ,,
51.
52<p>!!! ??? ,</p>
53.
54
55copyright should be escapable
56.
57\(c)
58.
59<p>(c)</p>
60.
61
62shouldn't replace entities
63.
64&#40;c) (c&#41; (c)
65.
66<p>(c) (c) ©</p>
67.
68
69
70dashes
71.
72---markdownit --- super---
73
74markdownit---awesome
75
76abc ----
77
78--markdownit -- super--
79
80markdownit--awesome
81.
82<p>—markdownit — super—</p>
83<p>markdownit—awesome</p>
84<p>abc ----</p>
85<p>–markdownit – super–</p>
86<p>markdownit–awesome</p>
87.
88
89dashes should be escapable
90.
91foo \-- bar
92
93foo -\- bar
94.
95<p>foo -- bar</p>
96<p>foo -- bar</p>
97.
98
99regression tests for #624
100.
1011---2---3
102
1031--2--3
104
1051 -- -- 3
106.
107<p>1—2—3</p>
108<p>1–2–3</p>
109<p>1 – – 3</p>
110.
crates/markdown-it/tests/fixtures/package.json created+6
...@@ -0,0 +1,6 @@
1{
2 "type": "module",
3 "dependencies": {
4 "markdown-it-testgen": "^0.1.6"
5 }
6}
crates/markdown-it/tests/fixtures/testgen.js created+99
...@@ -0,0 +1,99 @@
1// use `deno run --allow-read --allow-write ./fixtures/testgen.js ./commonmark.rs` to run this script
2
3if (Deno.args.length !== 1) {
4 console.error(`
5Usage: deno run --allow-read --allow-write ./fixtures/testgen.js ./commonmark.rs
6`)
7 Deno.exit(1)
8}
9
10import testgen from 'npm:markdown-it-testgen@^0.1.6'
11
12function rust_escape(s) {
13 if (s.match(/( $|\t)/m)) {
14 return '"' + s.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/ $/mg, '\\x20').replace(/\t/g, '\\t') + '"'
15 }
16
17 if (s.match(/#"|"#/)) return 'r##"' + s + '"##'
18 return 'r#"' + s + '"#'
19}
20
21let identmap = new Set()
22function ident(str) {
23 str = str.toLowerCase()
24 .replace(/[^a-z0-9]+/g, '_')
25 .replace(/^_+|_+$/g, '')
26
27 if (!str) str = 'unnamed'
28
29 let result = str
30 let idx = 0
31 while (identmap.has(result)) {
32 result = str + '_' + (++idx)
33 }
34
35 identmap.add(result)
36 return result
37}
38
39function generate(fixture) {
40 return `
41#[test]
42fn ${ident(fixture.header)}() {
43 let input = ${rust_escape(fixture.first.text.replace(/\n$/, ''))};
44 let output = ${rust_escape(fixture.second.text.replace(/\n$/, ''))};
45 run(input, output);
46}
47`.trim()
48}
49
50let input_raw = await Deno.readFile(Deno.args[0])
51let input = new TextDecoder().decode(input_raw)
52let lines = []
53let state = 'passthrough'
54
55for (let line of input.split('\n')) {
56 switch (state) {
57 case 'passthrough':
58 lines.push(line)
59 if (line.match(/^\/{4,}$/)) {
60 state = 'maybeheader'
61 }
62 break
63 case 'skipping':
64 if (line.match(/^\/{4,}$/)) {
65 lines.push(line)
66 state = 'maybeheader'
67 }
68 break
69 case 'maybeheader':
70 lines.push(line)
71 let match = line.match(/^\/{2,}\s+TESTGEN:\s*(.+)\s*$/)
72 if (match) {
73 let has_data = false
74 lines.push('#[rustfmt::skip]')
75 lines.push(`mod ${ident(match[1])} {`)
76 lines.push('use super::run;')
77 lines.push('// this part of the file is auto-generated')
78 lines.push('// don\'t edit it, otherwise your changes might be lost')
79 testgen.load(match[1], data => {
80 data.fixtures.forEach((data, idx) => {
81 has_data = true
82 if (idx++ > 0) lines.push('')
83 let generated = generate(data).replace(/\r?\n$/, '')
84 lines = lines.concat(generated.split('\n'))
85 })
86 })
87 lines.push('// end of auto-generated module')
88 lines.push('}')
89 if (!has_data) throw new Error(`no data found for ${match[1]}`)
90 state = 'skipping'
91 }
92 break
93 default:
94 throw Error('unknown state')
95 }
96}
97
98await Deno.rename(Deno.args[0], Deno.args[0] + '.old')
99await Deno.writeFile(Deno.args[0], new TextEncoder().encode(lines.join('\n') + '\n'))
crates/markdown-it/tests/linkify.rs created+170
...@@ -0,0 +1,170 @@
1#![cfg(feature = "linkify")]
2fn run(input: &str, output: &str) {
3 let output = if output.is_empty() { "".to_owned() } else { output.to_owned() + "\n" };
4 let md = &mut markdown_it::MarkdownIt::new();
5 markdown_it::plugins::cmark::add(md);
6 markdown_it::plugins::html::add(md);
7 markdown_it::plugins::extra::linkify::add(md);
8 let node = md.parse(&(input.to_owned() + "\n"));
9
10 // make sure we have sourcemaps for everything
11 node.walk(|node, _| assert!(node.srcmap.is_some()));
12
13 let result = node.render();
14 assert_eq!(result, output);
15
16 // make sure it doesn't crash without trailing \n
17 let _ = md.parse(input.trim_end());
18}
19
20#[test]
21fn linkify() {
22 let input = r#"url http://www.youtube.com/watch?v=5Jt5GEr4AYg."#;
23 let output = r#"<p>url <a href="http://www.youtube.com/watch?v=5Jt5GEr4AYg">http://www.youtube.com/watch?v=5Jt5GEr4AYg</a>.</p>"#;
24 run(input, output);
25}
26
27#[test]
28fn don_t_touch_text_in_links() {
29 let input = r#"[https://example.com](https://example.com)"#;
30 let output = r#"<p><a href="https://example.com">https://example.com</a></p>"#;
31 run(input, output);
32}
33
34#[test]
35fn don_t_touch_text_in_autolinks() {
36 let input = r#"<https://example.com>"#;
37 let output = r#"<p><a href="https://example.com">https://example.com</a></p>"#;
38 run(input, output);
39}
40
41#[test]
42fn don_t_touch_text_in_html_a_tags() {
43 let input = r#"<a href="https://example.com">https://example.com</a>"#;
44 let output = r#"<p><a href="https://example.com">https://example.com</a></p>"#;
45 run(input, output);
46}
47
48#[test]
49fn entities_inside_raw_links() {
50 let input = r#"https://example.com/foo&amp;bar"#;
51 let output = r#"<p><a href="https://example.com/foo&amp;amp;bar">https://example.com/foo&amp;amp;bar</a></p>"#;
52 run(input, output);
53}
54
55#[test]
56fn emphasis_inside_raw_links_asterisk_can_happen_in_links_with_params() {
57 let input = r#"https://example.com/foo*bar*baz"#;
58 let output = r#"<p><a href="https://example.com/foo*bar*baz">https://example.com/foo*bar*baz</a></p>"#;
59 run(input, output);
60}
61
62#[test]
63fn emphasis_inside_raw_links_underscore() {
64 let input = r#"http://example.org/foo._bar_-_baz"#;
65 let output = r#"<p><a href="http://example.org/foo._bar_-_baz">http://example.org/foo._bar_-_baz</a></p>"#;
66 run(input, output);
67}
68
69// not accepted as link by rust linkify
70/*#[test]
71fn backticks_inside_raw_links() {
72 let input = r#"https://example.com/foo`bar`baz"#;
73 let output = r#"<p><a href="https://example.com/foo%60bar%60baz">https://example.com/foo`bar`baz</a></p>"#;
74 run(input, output);
75}*/
76
77#[test]
78fn links_inside_raw_links() {
79 let input = r#"https://example.com/foo[123](456)bar"#;
80 let output = r#"<p><a href="https://example.com/foo%5B123%5D(456)bar">https://example.com/foo[123](456)bar</a></p>"#;
81 run(input, output);
82}
83
84#[test]
85fn escapes_not_allowed_at_the_start() {
86 let input = r#"\https://example.com"#;
87 let output = r#"<p>\https://example.com</p>"#;
88 run(input, output);
89}
90
91#[test]
92fn escapes_not_allowed_at_comma() {
93 let input = r#"https\://example.com"#;
94 let output = r#"<p>https://example.com</p>"#;
95 run(input, output);
96}
97
98#[test]
99fn escapes_not_allowed_at_slashes() {
100 let input = r#"https:\//aa.org https://bb.org"#;
101 let output = r#"<p>https://aa.org <a href="https://bb.org">https://bb.org</a></p>"#;
102 run(input, output);
103}
104
105#[test]
106fn fuzzy_link_shouldn_t_match_cc_org() {
107 let input = r#"https:/\/cc.org"#;
108 let output = r#"<p>https://cc.org</p>"#;
109 run(input, output);
110}
111
112#[test]
113fn bold_links_exclude_markup_of_pairs_from_link_tail() {
114 let input = r#"**http://example.com/foobar**"#;
115 let output = r#"<p><strong><a href="http://example.com/foobar">http://example.com/foobar</a></strong></p>"#;
116 run(input, output);
117}
118
119/*#[test]
120fn match_links_without_protocol() {
121 let input = r#"www.example.org"#;
122 let output = r#"<p><a href="http://www.example.org">www.example.org</a></p>"#;
123 run(input, output);
124}*/
125
126/*#[test]
127fn emails() {
128 let input = r#"test@example.com
129
130mailto:test@example.com"#;
131 let output = r#"<p><a href="mailto:test@example.com">test@example.com</a></p>
132<p><a href="mailto:test@example.com">mailto:test@example.com</a></p>"#;
133 run(input, output);
134}*/
135
136#[test]
137fn typorgapher_should_not_break_href() {
138 let input = r#"http://example.com/(c)"#;
139 let output = r#"<p><a href="http://example.com/(c)">http://example.com/(c)</a></p>"#;
140 run(input, output);
141}
142
143#[test]
144fn coverage_prefix_not_valid() {
145 let input = r#"http:/example.com/"#;
146 let output = r#"<p>http:/example.com/</p>"#;
147 run(input, output);
148}
149
150#[test]
151fn coverage_negative_link_level() {
152 let input = r#"</a>[https://example.com](https://example.com)"#;
153 let output = r#"<p></a><a href="https://example.com"><a href="https://example.com">https://example.com</a></a></p>"#;
154 run(input, output);
155}
156
157/*#[test]
158fn emphasis_with_real_link() {
159 let input = r#"http://cdecl.ridiculousfish.com/?q=int+%28*f%29+%28float+*%29%3B"#;
160 let output = r#"<p><a href="http://cdecl.ridiculousfish.com/?q=int+%28*f%29+%28float+*%29%3B">http://cdecl.ridiculousfish.com/?q=int+(*f)+(float+*)%3B</a></p>"#;
161 run(input, output);
162}*/
163
164#[test]
165fn emphasis_with_real_link_1() {
166 let input = r#"https://www.sell.fi/sites/default/files/elainlaakarilehti/tieteelliset_artikkelit/kahkonen_t._et_al.canine_pancreatitis-_review.pdf"#;
167 let output = r#"<p><a href="https://www.sell.fi/sites/default/files/elainlaakarilehti/tieteelliset_artikkelit/kahkonen_t._et_al.canine_pancreatitis-_review.pdf">https://www.sell.fi/sites/default/files/elainlaakarilehti/tieteelliset_artikkelit/kahkonen_t._et_al.canine_pancreatitis-_review.pdf</a></p>"#;
168 run(input, output);
169}
170
crates/markdown-it/tests/markdown-it-smartquotes.rs created+214
...@@ -0,0 +1,214 @@
1fn run(input: &str, output: &str) {
2 let output = if output.is_empty() {
3 "".to_owned()
4 } else {
5 output.to_owned() + "\n"
6 };
7 let md = &mut markdown_it::MarkdownIt::new();
8 markdown_it::plugins::cmark::add(md);
9 markdown_it::plugins::html::add(md);
10 markdown_it::plugins::extra::linkify::add(md);
11 markdown_it::plugins::extra::typographer::add(md);
12 markdown_it::plugins::extra::smartquotes::add(md);
13 let node = md.parse(&(input.to_owned() + "\n"));
14
15 // make sure we have sourcemaps for everything
16 node.walk(|node, _| assert!(node.srcmap.is_some()));
17
18 let result = node.render();
19 assert_eq!(result, output);
20
21 // make sure it doesn't crash without trailing \n
22 let _ = md.parse(input.trim_end());
23}
24///////////////////////////////////////////////////////////////////////////
25// TESTGEN: fixtures/markdown-it/smartquotes.txt
26#[rustfmt::skip]
27mod fixtures_markdown_it_smartquotes_txt {
28use super::run;
29// this part of the file is auto-generated
30// don't edit it, otherwise your changes might be lost
31#[test]
32fn should_parse_nested_quotes() {
33 let input = r#""foo 'bar' baz"
34
35'foo 'bar' baz'"#;
36 let output = r#"<p>“foo ‘bar’ baz”</p>
37<p>‘foo ‘bar’ baz’</p>"#;
38 run(input, output);
39}
40
41#[test]
42fn should_not_overlap_quotes() {
43 let input = r#"'foo "bar' baz""#;
44 let output = r#"<p>‘foo &quot;bar’ baz&quot;</p>"#;
45 run(input, output);
46}
47
48#[test]
49fn should_match_quotes_on_the_same_level() {
50 let input = r#""foo *bar* baz""#;
51 let output = r#"<p>“foo <em>bar</em> baz”</p>"#;
52 run(input, output);
53}
54
55#[test]
56fn should_handle_adjacent_nested_quotes() {
57 let input = r#"'"double in single"'
58
59"'single in double'""#;
60 let output = r#"<p>‘“double in single”’</p>
61<p>“‘single in double’”</p>"#;
62 run(input, output);
63}
64
65#[test]
66fn should_not_match_quotes_on_different_levels() {
67 let input = r#"*"foo* bar"
68
69"foo *bar"*
70
71*"foo* bar *baz"*"#;
72 let output = r#"<p><em>&quot;foo</em> bar&quot;</p>
73<p>&quot;foo <em>bar&quot;</em></p>
74<p><em>&quot;foo</em> bar <em>baz&quot;</em></p>"#;
75 run(input, output);
76}
77
78#[test]
79fn smartquotes_should_not_overlap_with_other_tags() {
80 let input = r#"*foo "bar* *baz" quux*"#;
81 let output = r#"<p><em>foo &quot;bar</em> <em>baz&quot; quux</em></p>"#;
82 run(input, output);
83}
84
85#[test]
86fn should_try_and_find_matching_quote_in_this_case() {
87 let input = r#""foo "bar 'baz""#;
88 let output = r#"<p>&quot;foo “bar 'baz”</p>"#;
89 run(input, output);
90}
91
92#[test]
93fn should_not_touch_inches_in_quotes() {
94 let input = r#""Monitor 21"" and "Monitor"""#;
95 let output = r#"<p>“Monitor 21&quot;” and “Monitor”&quot;</p>"#;
96 run(input, output);
97}
98
99#[test]
100fn should_render_an_apostrophe_as_a_rsquo() {
101 let input = r#"This isn't and can't be the best approach to implement this..."#;
102 let output = r#"<p>This isn’t and can’t be the best approach to implement this…</p>"#;
103 run(input, output);
104}
105
106#[test]
107fn apostrophe_could_end_the_word_that_s_why_original_smartypants_replaces_all_of_them_as_rsquo() {
108 let input = r#"users' stuff"#;
109 let output = r#"<p>users’ stuff</p>"#;
110 run(input, output);
111}
112
113#[test]
114fn quotes_between_punctuation_chars() {
115 let input = r#""(hai)"."#;
116 let output = r#"<p>“(hai)”.</p>"#;
117 run(input, output);
118}
119
120#[test]
121fn quotes_at_the_start_end_of_the_tokens() {
122 let input = r#""*foo* bar"
123
124"foo *bar*"
125
126"*foo bar*""#;
127 let output = r#"<p>“<em>foo</em> bar”</p>
128<p>“foo <em>bar</em>”</p>
129<p>“<em>foo bar</em>”</p>"#;
130 run(input, output);
131}
132
133#[test]
134fn should_treat_softbreak_as_a_space() {
135 let input = r#""this"
136and "that".
137
138"this" and
139"that"."#;
140 let output = r#"<p>“this”
141and “that”.</p>
142<p>“this” and
143“that”.</p>"#;
144 run(input, output);
145}
146
147#[test]
148fn should_treat_hardbreak_as_a_space() {
149 let input = r#""this"\
150and "that".
151
152"this" and\
153"that"."#;
154 let output = r#"<p>“this”<br>
155and “that”.</p>
156<p>“this” and<br>
157“that”.</p>"#;
158 run(input, output);
159}
160
161#[test]
162fn should_allow_quotes_adjacent_to_other_punctuation_characters_643() {
163 let input = r#"The dog---"'man's' best friend""#;
164 let output = r#"<p>The dog—“‘man’s’ best friend”</p>"#;
165 run(input, output);
166}
167
168#[test]
169fn should_parse_quotes_adjacent_to_code_block_677() {
170 let input = r#""test `code`"
171
172"`code` test""#;
173 let output = r#"<p>“test <code>code</code>”</p>
174<p>“<code>code</code> test”</p>"#;
175 run(input, output);
176}
177
178#[test]
179fn should_parse_quotes_adjacent_to_inline_html_677() {
180 let input = r#""test <br>"
181
182"<br> test""#;
183 let output = r#"<p>“test <br>”</p>
184<p>“<br> test”</p>"#;
185 run(input, output);
186}
187
188#[test]
189fn should_be_escapable() {
190 let input = r#""foo"
191
192\"foo"
193
194"foo\""#;
195 let output = r#"<p>“foo”</p>
196<p>&quot;foo&quot;</p>
197<p>&quot;foo&quot;</p>"#;
198 run(input, output);
199}
200
201#[test]
202fn should_not_replace_entities() {
203 let input = r#"&quot;foo&quot;
204
205&quot;foo"
206
207"foo&quot;"#;
208 let output = r#"<p>&quot;foo&quot;</p>
209<p>&quot;foo&quot;</p>
210<p>&quot;foo&quot;</p>"#;
211 run(input, output);
212}
213// end of auto-generated module
214}
crates/markdown-it/tests/markdown-it-typographer.rs created+157
...@@ -0,0 +1,157 @@
1fn run(input: &str, output: &str) {
2 let output = if output.is_empty() {
3 "".to_owned()
4 } else {
5 output.to_owned() + "\n"
6 };
7 let md = &mut markdown_it::MarkdownIt::new();
8 markdown_it::plugins::cmark::add(md);
9 markdown_it::plugins::html::add(md);
10 markdown_it::plugins::extra::linkify::add(md);
11 markdown_it::plugins::extra::typographer::add(md);
12 let node = md.parse(&(input.to_owned() + "\n"));
13
14 // make sure we have sourcemaps for everything
15 node.walk(|node, _| assert!(node.srcmap.is_some()));
16
17 let result = node.render();
18 assert_eq!(result, output);
19
20 // make sure it doesn't crash without trailing \n
21 let _ = md.parse(input.trim_end());
22}
23///////////////////////////////////////////////////////////////////////////
24// TESTGEN: fixtures/markdown-it/typographer-extra.txt
25#[rustfmt::skip]
26mod fixtures_markdown_it_typographer_extra_txt {
27use super::run;
28// this part of the file is auto-generated
29// don't edit it, otherwise your changes might be lost
30#[test]
31fn don_t_touch_text_in_autolinks() {
32 let input = r#"URL with (C) (c) (R) (r) (TM) (tm): https://example.com/(c)(r)(tm)/(C)(R)(TM) what do you think?"#;
33 let output = r#"<p>URL with © © ® ® ™ ™: <a href="https://example.com/(c)(r)(tm)/(C)(R)(TM)">https://example.com/(c)(r)(tm)/(C)(R)(TM)</a> what do you think?</p>"#;
34 run(input, output);
35}
36
37#[test]
38fn replacements_for_tm_should_allow_mixed_case_tm_and_tm() {
39 let input = r#"These two should both end up the same as (TM) and (tm): (tM), (Tm)."#;
40 let output = r#"<p>These two should both end up the same as ™ and ™: ™, ™.</p>"#;
41 run(input, output);
42}
43// end of auto-generated module
44}
45///////////////////////////////////////////////////////////////////////////
46// TESTGEN: fixtures/markdown-it/typographer.txt
47#[rustfmt::skip]
48mod fixtures_markdown_it_typographer_txt {
49use super::run;
50// this part of the file is auto-generated
51// don't edit it, otherwise your changes might be lost
52#[test]
53fn unnamed() {
54 let input = r#"(bad)"#;
55 let output = r#"<p>(bad)</p>"#;
56 run(input, output);
57}
58
59#[test]
60fn copyright() {
61 let input = r#"(c) (C)"#;
62 let output = r#"<p>© ©</p>"#;
63 run(input, output);
64}
65
66#[test]
67fn reserved() {
68 let input = r#"(r) (R)"#;
69 let output = r#"<p>® ®</p>"#;
70 run(input, output);
71}
72
73#[test]
74fn trademark() {
75 let input = r#"(tm) (TM)"#;
76 let output = r#"<p>™ ™</p>"#;
77 run(input, output);
78}
79
80#[test]
81fn plus_minus() {
82 let input = r#"+-5"#;
83 let output = r#"<p>±5</p>"#;
84 run(input, output);
85}
86
87#[test]
88fn ellipsis() {
89 let input = r#"test.. test... test..... test?..... test!...."#;
90 let output = r#"<p>test… test… test… test?.. test!..</p>"#;
91 run(input, output);
92}
93
94#[test]
95fn dupes() {
96 let input = r#"!!!!!! ???? ,,"#;
97 let output = r#"<p>!!! ??? ,</p>"#;
98 run(input, output);
99}
100
101#[test]
102fn copyright_should_be_escapable() {
103 let input = r#"\(c)"#;
104 let output = r#"<p>(c)</p>"#;
105 run(input, output);
106}
107
108#[test]
109fn shouldn_t_replace_entities() {
110 let input = r#"&#40;c) (c&#41; (c)"#;
111 let output = r#"<p>(c) (c) ©</p>"#;
112 run(input, output);
113}
114
115#[test]
116fn dashes() {
117 let input = r#"---markdownit --- super---
118
119markdownit---awesome
120
121abc ----
122
123--markdownit -- super--
124
125markdownit--awesome"#;
126 let output = r#"<p>—markdownit — super—</p>
127<p>markdownit—awesome</p>
128<p>abc ----</p>
129<p>–markdownit – super–</p>
130<p>markdownit–awesome</p>"#;
131 run(input, output);
132}
133
134#[test]
135fn dashes_should_be_escapable() {
136 let input = r#"foo \-- bar
137
138foo -\- bar"#;
139 let output = r#"<p>foo -- bar</p>
140<p>foo -- bar</p>"#;
141 run(input, output);
142}
143
144#[test]
145fn regression_tests_for_624() {
146 let input = r#"1---2---3
147
1481--2--3
149
1501 -- -- 3"#;
151 let output = r#"<p>1—2—3</p>
152<p>1–2–3</p>
153<p>1 – – 3</p>"#;
154 run(input, output);
155}
156// end of auto-generated module
157}
crates/markdown-it/tests/markdown-it.rs created+828
...@@ -0,0 +1,828 @@
1
2fn run(input: &str, output: &str) {
3 let output = if output.is_empty() { "".to_owned() } else { output.to_owned() + "\n" };
4 let md = &mut markdown_it::MarkdownIt::new();
5 markdown_it::plugins::cmark::add(md);
6 markdown_it::plugins::html::add(md);
7 markdown_it::plugins::extra::typographer::add(md);
8 markdown_it::plugins::extra::tables::add(md);
9 let node = md.parse(&(input.to_owned() + "\n"));
10
11 // make sure we have sourcemaps for everything
12 node.walk(|node, _| assert!(node.srcmap.is_some()));
13
14 let result = node.render();
15 assert_eq!(result, output);
16
17 // make sure it doesn't crash without trailing \n
18 let _ = md.parse(input.trim_end());
19}
20
21///////////////////////////////////////////////////////////////////////////
22// TESTGEN: fixtures/markdown-it/tables.txt
23#[rustfmt::skip]
24mod fixtures_markdown_it_tables_txt {
25use super::run;
26// this part of the file is auto-generated
27// don't edit it, otherwise your changes might be lost
28#[test]
29fn simple() {
30 let input = r#"| Heading 1 | Heading 2
31| --------- | ---------
32| Cell 1 | Cell 2
33| Cell 3 | Cell 4"#;
34 let output = r#"<table>
35<thead>
36<tr>
37<th>Heading 1</th>
38<th>Heading 2</th>
39</tr>
40</thead>
41<tbody>
42<tr>
43<td>Cell 1</td>
44<td>Cell 2</td>
45</tr>
46<tr>
47<td>Cell 3</td>
48<td>Cell 4</td>
49</tr>
50</tbody>
51</table>"#;
52 run(input, output);
53}
54
55#[test]
56fn column_alignment() {
57 let input = r#"| Header 1 | Header 2 | Header 3 | Header 4 |
58| :------: | -------: | :------- | -------- |
59| Cell 1 | Cell 2 | Cell 3 | Cell 4 |
60| Cell 5 | Cell 6 | Cell 7 | Cell 8 |"#;
61 let output = r#"<table>
62<thead>
63<tr>
64<th style="text-align:center">Header 1</th>
65<th style="text-align:right">Header 2</th>
66<th style="text-align:left">Header 3</th>
67<th>Header 4</th>
68</tr>
69</thead>
70<tbody>
71<tr>
72<td style="text-align:center">Cell 1</td>
73<td style="text-align:right">Cell 2</td>
74<td style="text-align:left">Cell 3</td>
75<td>Cell 4</td>
76</tr>
77<tr>
78<td style="text-align:center">Cell 5</td>
79<td style="text-align:right">Cell 6</td>
80<td style="text-align:left">Cell 7</td>
81<td>Cell 8</td>
82</tr>
83</tbody>
84</table>"#;
85 run(input, output);
86}
87
88#[test]
89fn nested_emphases() {
90 let input = r#"Header 1|Header 2|Header 3|Header 4
91:-------|:------:|-------:|--------
92Cell 1 |Cell 2 |Cell 3 |Cell 4
93*Cell 5*|Cell 6 |Cell 7 |Cell 8"#;
94 let output = r#"<table>
95<thead>
96<tr>
97<th style="text-align:left">Header 1</th>
98<th style="text-align:center">Header 2</th>
99<th style="text-align:right">Header 3</th>
100<th>Header 4</th>
101</tr>
102</thead>
103<tbody>
104<tr>
105<td style="text-align:left">Cell 1</td>
106<td style="text-align:center">Cell 2</td>
107<td style="text-align:right">Cell 3</td>
108<td>Cell 4</td>
109</tr>
110<tr>
111<td style="text-align:left"><em>Cell 5</em></td>
112<td style="text-align:center">Cell 6</td>
113<td style="text-align:right">Cell 7</td>
114<td>Cell 8</td>
115</tr>
116</tbody>
117</table>"#;
118 run(input, output);
119}
120
121#[test]
122fn nested_tables_inside_blockquotes() {
123 let input = r#"> foo|foo
124> ---|---
125> bar|bar
126baz|baz"#;
127 let output = r#"<blockquote>
128<table>
129<thead>
130<tr>
131<th>foo</th>
132<th>foo</th>
133</tr>
134</thead>
135<tbody>
136<tr>
137<td>bar</td>
138<td>bar</td>
139</tr>
140</tbody>
141</table>
142</blockquote>
143<p>baz|baz</p>"#;
144 run(input, output);
145}
146
147#[test]
148fn minimal_one_column() {
149 let input = r#"| foo
150|----
151| test2"#;
152 let output = r#"<table>
153<thead>
154<tr>
155<th>foo</th>
156</tr>
157</thead>
158<tbody>
159<tr>
160<td>test2</td>
161</tr>
162</tbody>
163</table>"#;
164 run(input, output);
165}
166
167#[test]
168fn this_is_parsed_as_one_big_table() {
169 let input = r#"- foo|foo
170---|---
171bar|bar"#;
172 let output = r#"<table>
173<thead>
174<tr>
175<th>- foo</th>
176<th>foo</th>
177</tr>
178</thead>
179<tbody>
180<tr>
181<td>bar</td>
182<td>bar</td>
183</tr>
184</tbody>
185</table>"#;
186 run(input, output);
187}
188
189#[test]
190fn second_line_should_not_contain_symbols_except_and() {
191 let input = r#"foo|foo
192-----|-----s
193bar|bar"#;
194 let output = r#"<p>foo|foo
195-----|-----s
196bar|bar</p>"#;
197 run(input, output);
198}
199
200#[test]
201fn second_line_should_contain_symbol() {
202 let input = r#"foo|foo
203-----:-----
204bar|bar"#;
205 let output = r#"<p>foo|foo
206-----:-----
207bar|bar</p>"#;
208 run(input, output);
209}
210
211#[test]
212fn second_line_should_not_have_empty_columns_in_the_middle() {
213 let input = r#"foo|foo
214-----||-----
215bar|bar"#;
216 let output = r#"<p>foo|foo
217-----||-----
218bar|bar</p>"#;
219 run(input, output);
220}
221
222#[test]
223fn wrong_alignment_symbol_position() {
224 let input = r#"foo|foo
225-----|-::-
226bar|bar"#;
227 let output = r#"<p>foo|foo
228-----|-::-
229bar|bar</p>"#;
230 run(input, output);
231}
232
233#[test]
234fn title_line_should_contain_symbol() {
235 let input = r#"foo
236-----|-----
237bar|bar"#;
238 let output = r#"<p>foo
239-----|-----
240bar|bar</p>"#;
241 run(input, output);
242}
243
244#[test]
245fn allow_tabs_as_a_separator_on_2nd_line() {
246 let input = "|\tfoo\t|\tbar\t|
247|\t---\t|\t---\t|
248|\tbaz\t|\tquux\t|";
249 let output = r#"<table>
250<thead>
251<tr>
252<th>foo</th>
253<th>bar</th>
254</tr>
255</thead>
256<tbody>
257<tr>
258<td>baz</td>
259<td>quux</td>
260</tr>
261</tbody>
262</table>"#;
263 run(input, output);
264}
265
266#[test]
267fn should_terminate_paragraph() {
268 let input = r#"paragraph
269foo|foo
270---|---
271bar|bar"#;
272 let output = r#"<p>paragraph</p>
273<table>
274<thead>
275<tr>
276<th>foo</th>
277<th>foo</th>
278</tr>
279</thead>
280<tbody>
281<tr>
282<td>bar</td>
283<td>bar</td>
284</tr>
285</tbody>
286</table>"#;
287 run(input, output);
288}
289
290#[test]
291fn another_complicated_backticks_case() {
292 let input = r#"| Heading 1 | Heading 2
293| --------- | ---------
294| Cell 1 | Cell 2
295| \\\`|\\\`"#;
296 let output = r#"<table>
297<thead>
298<tr>
299<th>Heading 1</th>
300<th>Heading 2</th>
301</tr>
302</thead>
303<tbody>
304<tr>
305<td>Cell 1</td>
306<td>Cell 2</td>
307</tr>
308<tr>
309<td>\`</td>
310<td>\`</td>
311</tr>
312</tbody>
313</table>"#;
314 run(input, output);
315}
316
317#[test]
318fn in_tables_should_not_count_as_escaped_backtick() {
319 let input = r#"# | 1 | 2
320--|--|--
321x | `\` | `x`"#;
322 let output = r#"<table>
323<thead>
324<tr>
325<th>#</th>
326<th>1</th>
327<th>2</th>
328</tr>
329</thead>
330<tbody>
331<tr>
332<td>x</td>
333<td><code>\</code></td>
334<td><code>x</code></td>
335</tr>
336</tbody>
337</table>"#;
338 run(input, output);
339}
340
341#[test]
342fn tables_should_handle_escaped_backticks() {
343 let input = r#"# | 1 | 2
344--|--|--
345x | \`\` | `x`"#;
346 let output = r#"<table>
347<thead>
348<tr>
349<th>#</th>
350<th>1</th>
351<th>2</th>
352</tr>
353</thead>
354<tbody>
355<tr>
356<td>x</td>
357<td>``</td>
358<td><code>x</code></td>
359</tr>
360</tbody>
361</table>"#;
362 run(input, output);
363}
364
365#[test]
366fn an_amount_of_rows_might_be_different_across_the_table_issue_171() {
367 let input = r#"| 1 | 2 |
368| :-----: | :-----: |
369| 3 | 4 | 5 | 6 |"#;
370 let output = r#"<table>
371<thead>
372<tr>
373<th style="text-align:center">1</th>
374<th style="text-align:center">2</th>
375</tr>
376</thead>
377<tbody>
378<tr>
379<td style="text-align:center">3</td>
380<td style="text-align:center">4</td>
381</tr>
382</tbody>
383</table>"#;
384 run(input, output);
385}
386
387#[test]
388fn an_amount_of_rows_might_be_different_across_the_table_2() {
389 let input = r#"| 1 | 2 | 3 | 4 |
390| :-----: | :-----: | :-----: | :-----: |
391| 5 | 6 |"#;
392 let output = r#"<table>
393<thead>
394<tr>
395<th style="text-align:center">1</th>
396<th style="text-align:center">2</th>
397<th style="text-align:center">3</th>
398<th style="text-align:center">4</th>
399</tr>
400</thead>
401<tbody>
402<tr>
403<td style="text-align:center">5</td>
404<td style="text-align:center">6</td>
405<td style="text-align:center"></td>
406<td style="text-align:center"></td>
407</tr>
408</tbody>
409</table>"#;
410 run(input, output);
411}
412
413#[test]
414fn allow_one_column_tables_issue_171() {
415 let input = r#"| foo |
416:-----:
417| bar |"#;
418 let output = r#"<table>
419<thead>
420<tr>
421<th style="text-align:center">foo</th>
422</tr>
423</thead>
424<tbody>
425<tr>
426<td style="text-align:center">bar</td>
427</tr>
428</tbody>
429</table>"#;
430 run(input, output);
431}
432
433#[test]
434fn allow_indented_tables_issue_325() {
435 let input = r#" | Col1a | Col2a |
436 | ----- | ----- |
437 | Col1b | Col2b |"#;
438 let output = r#"<table>
439<thead>
440<tr>
441<th>Col1a</th>
442<th>Col2a</th>
443</tr>
444</thead>
445<tbody>
446<tr>
447<td>Col1b</td>
448<td>Col2b</td>
449</tr>
450</tbody>
451</table>"#;
452 run(input, output);
453}
454
455#[test]
456fn tables_should_not_be_indented_more_than_4_spaces_1st_line() {
457 let input = r#" | Col1a | Col2a |
458 | ----- | ----- |
459 | Col1b | Col2b |"#;
460 let output = r#"<pre><code>| Col1a | Col2a |
461</code></pre>
462<p>| ----- | ----- |
463| Col1b | Col2b |</p>"#;
464 run(input, output);
465}
466
467#[test]
468fn tables_should_not_be_indented_more_than_4_spaces_2nd_line() {
469 let input = r#" | Col1a | Col2a |
470 | ----- | ----- |
471 | Col1b | Col2b |"#;
472 let output = r#"<p>| Col1a | Col2a |
473| ----- | ----- |
474| Col1b | Col2b |</p>"#;
475 run(input, output);
476}
477
478#[test]
479fn tables_should_not_be_indented_more_than_4_spaces_3rd_line() {
480 let input = r#" | Col1a | Col2a |
481 | ----- | ----- |
482 | Col1b | Col2b |"#;
483 let output = r#"<table>
484<thead>
485<tr>
486<th>Col1a</th>
487<th>Col2a</th>
488</tr>
489</thead>
490</table>
491<pre><code>| Col1b | Col2b |
492</code></pre>"#;
493 run(input, output);
494}
495
496#[test]
497fn allow_tables_with_empty_body() {
498 let input = r#" | Col1a | Col2a |
499 | ----- | ----- |"#;
500 let output = r#"<table>
501<thead>
502<tr>
503<th>Col1a</th>
504<th>Col2a</th>
505</tr>
506</thead>
507</table>"#;
508 run(input, output);
509}
510
511#[test]
512fn align_row_should_be_at_least_as_large_as_any_actual_rows() {
513 let input = r#"Col1a | Col1b | Col1c
514----- | -----
515Col2a | Col2b | Col2c"#;
516 let output = r#"<p>Col1a | Col1b | Col1c
517----- | -----
518Col2a | Col2b | Col2c</p>"#;
519 run(input, output);
520}
521
522#[test]
523fn escaped_pipes_inside_backticks_don_t_split_cells() {
524 let input = r#"| Heading 1 | Heading 2
525| --------- | ---------
526| Cell 1 | Cell 2
527| `Cell 3\|` | Cell 4"#;
528 let output = r#"<table>
529<thead>
530<tr>
531<th>Heading 1</th>
532<th>Heading 2</th>
533</tr>
534</thead>
535<tbody>
536<tr>
537<td>Cell 1</td>
538<td>Cell 2</td>
539</tr>
540<tr>
541<td><code>Cell 3|</code></td>
542<td>Cell 4</td>
543</tr>
544</tbody>
545</table>"#;
546 run(input, output);
547}
548
549#[test]
550fn escape_before_escaped_pipes_inside_backticks_don_t_split_cells() {
551 let input = r#"| Heading 1 | Heading 2
552| --------- | ---------
553| Cell 1 | Cell 2
554| `Cell 3\\|` | Cell 4"#;
555 let output = r#"<table>
556<thead>
557<tr>
558<th>Heading 1</th>
559<th>Heading 2</th>
560</tr>
561</thead>
562<tbody>
563<tr>
564<td>Cell 1</td>
565<td>Cell 2</td>
566</tr>
567<tr>
568<td><code>Cell 3\|</code></td>
569<td>Cell 4</td>
570</tr>
571</tbody>
572</table>"#;
573 run(input, output);
574}
575
576#[test]
577fn regression_test_for_721_table_in_a_list_indented_with_tabs() {
578 let input = "- Level 1
579
580\t- Level 2
581
582\t\t| Column 1 | Column 2 |
583\t\t| -------- | -------- |
584\t\t| abcdefgh | ijklmnop |";
585 let output = r#"<ul>
586<li>
587<p>Level 1</p>
588<ul>
589<li>
590<p>Level 2</p>
591<table>
592<thead>
593<tr>
594<th>Column 1</th>
595<th>Column 2</th>
596</tr>
597</thead>
598<tbody>
599<tr>
600<td>abcdefgh</td>
601<td>ijklmnop</td>
602</tr>
603</tbody>
604</table>
605</li>
606</ul>
607</li>
608</ul>"#;
609 run(input, output);
610}
611
612#[test]
613fn table_without_any_columns_is_not_a_table_724() {
614 let input = r#"|
615|
616|"#;
617 let output = r#"<p>|
618|
619|</p>"#;
620 run(input, output);
621}
622
623#[test]
624fn gfm_4_10_tables_extension_example_198() {
625 let input = r#"| foo | bar |
626| --- | --- |
627| baz | bim |"#;
628 let output = r#"<table>
629<thead>
630<tr>
631<th>foo</th>
632<th>bar</th>
633</tr>
634</thead>
635<tbody>
636<tr>
637<td>baz</td>
638<td>bim</td>
639</tr>
640</tbody>
641</table>"#;
642 run(input, output);
643}
644
645#[test]
646fn gfm_4_10_tables_extension_example_199() {
647 let input = r#"| abc | defghi |
648:-: | -----------:
649bar | baz"#;
650 let output = r#"<table>
651<thead>
652<tr>
653<th style="text-align:center">abc</th>
654<th style="text-align:right">defghi</th>
655</tr>
656</thead>
657<tbody>
658<tr>
659<td style="text-align:center">bar</td>
660<td style="text-align:right">baz</td>
661</tr>
662</tbody>
663</table>"#;
664 run(input, output);
665}
666
667#[test]
668fn gfm_4_10_tables_extension_example_200() {
669 let input = r#"| f\|oo |
670| ------ |
671| b `\|` az |
672| b **\|** im |"#;
673 let output = r#"<table>
674<thead>
675<tr>
676<th>f|oo</th>
677</tr>
678</thead>
679<tbody>
680<tr>
681<td>b <code>|</code> az</td>
682</tr>
683<tr>
684<td>b <strong>|</strong> im</td>
685</tr>
686</tbody>
687</table>"#;
688 run(input, output);
689}
690
691#[test]
692fn gfm_4_10_tables_extension_example_201() {
693 let input = r#"| abc | def |
694| --- | --- |
695| bar | baz |
696> bar"#;
697 let output = r#"<table>
698<thead>
699<tr>
700<th>abc</th>
701<th>def</th>
702</tr>
703</thead>
704<tbody>
705<tr>
706<td>bar</td>
707<td>baz</td>
708</tr>
709</tbody>
710</table>
711<blockquote>
712<p>bar</p>
713</blockquote>"#;
714 run(input, output);
715}
716
717#[test]
718fn gfm_4_10_tables_extension_example_202() {
719 let input = r#"| abc | def |
720| --- | --- |
721| bar | baz |
722bar
723
724bar"#;
725 let output = r#"<table>
726<thead>
727<tr>
728<th>abc</th>
729<th>def</th>
730</tr>
731</thead>
732<tbody>
733<tr>
734<td>bar</td>
735<td>baz</td>
736</tr>
737<tr>
738<td>bar</td>
739<td></td>
740</tr>
741</tbody>
742</table>
743<p>bar</p>"#;
744 run(input, output);
745}
746
747#[test]
748fn gfm_4_10_tables_extension_example_203() {
749 let input = r#"| abc | def |
750| --- |
751| bar |"#;
752 let output = r#"<p>| abc | def |
753| — |
754| bar |</p>"#;
755 run(input, output);
756}
757
758#[test]
759fn gfm_4_10_tables_extension_example_204() {
760 let input = r#"| abc | def |
761| --- | --- |
762| bar |
763| bar | baz | boo |"#;
764 let output = r#"<table>
765<thead>
766<tr>
767<th>abc</th>
768<th>def</th>
769</tr>
770</thead>
771<tbody>
772<tr>
773<td>bar</td>
774<td></td>
775</tr>
776<tr>
777<td>bar</td>
778<td>baz</td>
779</tr>
780</tbody>
781</table>"#;
782 run(input, output);
783}
784
785#[test]
786fn gfm_4_10_tables_extension_example_205() {
787 let input = r#"| abc | def |
788| --- | --- |"#;
789 let output = r#"<table>
790<thead>
791<tr>
792<th>abc</th>
793<th>def</th>
794</tr>
795</thead>
796</table>"#;
797 run(input, output);
798}
799
800#[test]
801fn a_list_takes_precedence_in_case_of_ambiguity() {
802 let input = r#"a | b
803- | -
8041 | 2"#;
805 let output = r#"<p>a | b</p>
806<ul>
807<li>| -
8081 | 2</li>
809</ul>"#;
810 run(input, output);
811}
812// end of auto-generated module
813}
814///////////////////////////////////////////////////////////////////////////
815// TESTGEN: fixtures/markdown-it/markdown-it-rs.txt
816#[rustfmt::skip]
817mod fixtures_markdown_it_markdown_it_rs_txt {
818use super::run;
819// this part of the file is auto-generated
820// don't edit it, otherwise your changes might be lost
821#[test]
822fn regression_test_for_panic_in_40() {
823 let input = r#"![hello'"`world](x)\"#;
824 let output = r#"<p><img src="x" alt="hello'&quot;`world">\</p>"#;
825 run(input, output);
826}
827// end of auto-generated module
828}
crates/markdown-it/tests/pathological.rs created+140
...@@ -0,0 +1,140 @@
1// run it like this:
2// cargo test --test pathological --jobs 1 -- --nocapture --test-threads=1
3use markdown_it::MarkdownIt;
4use once_cell::sync::Lazy;
5use std::time::SystemTime;
6
7static MD : Lazy<MarkdownIt> = Lazy::new(|| {
8 let mut parser = markdown_it::MarkdownIt::new();
9 markdown_it::plugins::cmark::add(&mut parser);
10 markdown_it::plugins::html::add(&mut parser);
11 markdown_it::plugins::extra::add(&mut parser);
12 parser
13});
14
15fn run(src: &str) {
16 let now = SystemTime::now();
17 MD.parse(src);
18 dbg!(now.elapsed().ok().unwrap());
19}
20
21
22mod commonmark {
23 // Ported from cmark, https://github.com/commonmark/cmark/blob/master/test/pathological_tests.py
24 use super::run;
25
26 #[test]
27 fn nested_inlines() {
28 run(&format!("{}{}{}", "*".repeat(100000), "a", "*".repeat(100000)));
29 }
30
31 #[test]
32 fn nested_strong_emph() {
33 // suspiciously slow
34 run(&format!("{}{}{}", "*a **a".repeat(5000), "b", " a** a*".repeat(5000)));
35 }
36
37 #[test]
38 fn many_emph_closers_with_no_openers() {
39 run(&"a_ ".repeat(100000));
40 }
41
42 #[test]
43 fn many_emph_openers_with_no_closers() {
44 run(&"_a ".repeat(100000));
45 }
46
47 #[test]
48 fn many_link_closers_with_no_openers() {
49 run(&"a]".repeat(100000));
50 }
51
52 #[test]
53 fn many_link_openers_with_no_closers() {
54 run(&"[a".repeat(50000));
55 }
56
57 #[test]
58 fn mismatched_openers_and_closers() {
59 // most probably a bug
60 run(&"*a_ ".repeat(50000));
61 }
62
63 #[test]
64 fn commonmark_cmark_389() {
65 run(&format!("{}{}", "*a ".repeat(2000), "_a*_ ".repeat(2000)));
66 }
67
68 #[test]
69 fn openers_and_closers_multiple_of_3() {
70 run(&format!("{}{}", "a**b", "c* ".repeat(50000)));
71 }
72
73 #[test]
74 fn link_openers_and_emph_closers() {
75 run(&"[ a_".repeat(50000));
76 }
77
78 #[test]
79 fn link_pattern_repeated() {
80 run(&"[ (](".repeat(100000));
81 }
82
83 #[test]
84 fn nested_brackets() {
85 run(&format!("{}{}{}", "[".repeat(50000), "a", "]".repeat(50000)));
86 }
87
88 #[test]
89 fn nested_block_quotes() {
90 run(&format!("{}{}", "> ".repeat(50000), "a"));
91 }
92
93 #[test]
94 fn deeply_nested_lists() {
95 let src = (0..5000).map(|x| format!("{}{}", " ".repeat(x), "* a\n")).collect::<Vec<_>>().join("");
96 run(&src);
97 }
98
99 #[test]
100 fn backticks() {
101 let src = (0..1000).map(|x| format!("{}{}", "e", "`".repeat(x))).collect::<Vec<_>>().join("");
102 run(&src);
103 }
104
105 #[test]
106 fn unclosed_links_a() {
107 run(&"[a](<b".repeat(30000));
108 }
109
110 #[test]
111 fn unclosed_links_b() {
112 run(&"[a](b".repeat(30000));
113 }
114}
115
116mod markdownit {
117 // Ported from markdown-it.js
118 use super::run;
119
120 #[test]
121 fn emphasis_pattern() {
122 run(&"**_* ".repeat(5000));
123 }
124
125 #[test]
126 fn backtick_pattern() {
127 run(&"``\\".repeat(50000));
128 }
129
130 #[test]
131 fn autolinks_pattern() {
132 run(&format!("{}{}", "<".repeat(100000), ">"));
133 }
134
135 #[test]
136 fn hardbreak_whitespaces_pattern() {
137 run(&format!("{}{}{}", "x", " ".repeat(100000), "x \nx"));
138 }
139}
140
crates/markdown-it/tests/sourcemaps.rs created+376
...@@ -0,0 +1,376 @@
1use markdown_it::Node;
2use markdown_it::common::sourcemap::SourceWithLineStarts;
3
4fn run(input: &str, f: fn (&Node, SourceWithLineStarts)) {
5 let md = &mut markdown_it::MarkdownIt::new();
6 markdown_it::plugins::cmark::add(md);
7 markdown_it::plugins::html::add(md);
8 let node = md.parse(input);
9 node.walk(|node, _| assert!(node.srcmap.is_some()));
10 f(&node, SourceWithLineStarts::new(input));
11}
12
13fn getmap(node: &Node, map: &SourceWithLineStarts) -> ((u32, u32), (u32, u32)) {
14 node.srcmap.unwrap().get_positions(map)
15}
16
17#[test]
18fn paragraph() {
19 // same as commonmark.js
20 run("foo \n \n \n\n barbaz\n\tquux \n", |node, map| {
21 assert_eq!(
22 getmap(&node.children[0], &map),
23 ((1, 1), (1, 6)),
24 );
25 assert_eq!(
26 getmap(&node.children[1], &map),
27 ((5, 3), (6, 8)),
28 );
29 });
30}
31
32#[test]
33fn hr() {
34 // same as commonmark.js
35 run(" --- \n\n * * *\n", |node, map| {
36 assert_eq!(
37 getmap(&node.children[0], &map),
38 ((1, 2), (1, 6)),
39 );
40 assert_eq!(
41 getmap(&node.children[1], &map),
42 ((3, 3), (3, 7)),
43 );
44 });
45}
46
47#[test]
48fn heading() {
49 // same as commonmark.js
50 run(" \n ### foo ### \n\n", |node, map| {
51 assert_eq!(
52 getmap(&node.children[0], &map),
53 ((2, 3), (2, 15)),
54 );
55 });
56
57 run(" #\n", |node, map| {
58 assert_eq!(
59 getmap(&node.children[0], &map),
60 ((1, 3), (1, 3)),
61 );
62 });
63}
64
65#[test]
66fn lheading() {
67 // same as commonmark.js
68 run(" foo\n bar\n ----\n\n", |node, map| {
69 assert_eq!(
70 getmap(&node.children[0], &map),
71 ((1, 3), (3, 5)),
72 );
73 });
74}
75
76#[test]
77fn fence() {
78 // same as commonmark.js
79 run(" ~~~ foo ~~~\n", |node, map| {
80 assert_eq!(
81 getmap(&node.children[0], &map),
82 ((1, 3), (1, 13)),
83 );
84 });
85
86 run(" ```\n 12\n", |node, map| {
87 assert_eq!(
88 getmap(&node.children[0], &map),
89 ((1, 3), (2, 3)),
90 );
91 });
92
93 run("```\n\n\n\n", |node, map| {
94 assert_eq!(
95 getmap(&node.children[0], &map),
96 ((1, 1), (4, 0)),
97 );
98 });
99
100 run("~~~\na\nb\n~~~ \nc\n", |node, map| {
101 assert_eq!(
102 getmap(&node.children[0], &map),
103 ((1, 1), (4, 5)),
104 );
105 });
106}
107
108#[test]
109fn html_block() {
110 // same as commonmark.js
111 run(" <div>\n", |node, map| {
112 assert_eq!(
113 getmap(&node.children[0], &map),
114 ((1, 3), (1, 7)),
115 );
116 });
117
118 run("<div>\n</div> \n", |node, map| {
119 assert_eq!(
120 getmap(&node.children[0], &map),
121 ((1, 1), (2, 8)),
122 );
123 });
124}
125
126#[test]
127fn code_block() {
128 // this should be (1, 5), (1, 9)
129 // for simplicity, we point source maps for block tags to first
130 // nonspace character, but it isn't quite correct for code blocks
131 run(" foo\n", |node, map| {
132 assert_eq!(
133 getmap(&node.children[0], &map),
134 ((1, 7), (1, 9)),
135 );
136 });
137
138 run(" a\n b\n c\n", |node, map| {
139 assert_eq!(
140 getmap(&node.children[0], &map),
141 ((1, 4), (3, 6)),
142 );
143 });
144
145 // this I believe to be error in commonmark, code block
146 // only have 1 line as per spec, but cmark reports 3 lines
147 run(" foobar \n \n \n\nbar\n", |node, map| {
148 assert_eq!(
149 getmap(&node.children[0], &map),
150 ((1, 5), (1, 12)),
151 );
152 });
153}
154
155#[test]
156fn blockquotes() {
157 // same as commonmark.js
158 run(" > foo \n", |node, map| {
159 assert_eq!(
160 getmap(&node.children[0], &map),
161 ((1, 3), (1, 9)),
162 );
163 });
164
165 run("> foo\nbar\n\n", |node, map| {
166 assert_eq!(
167 getmap(&node.children[0], &map),
168 ((1, 1), (2, 3)),
169 );
170 });
171}
172
173#[test]
174fn lists() {
175 // same as commonmark.js
176 run(" 1. foo\n 2. bar\n", |node, map| {
177 assert_eq!(
178 getmap(&node.children[0], &map),
179 ((1, 2), (2, 7)),
180 );
181
182 assert_eq!(
183 getmap(&node.children[0].children[0], &map),
184 ((1, 2), (1, 7)),
185 );
186 });
187
188 run(" - foo\n\n - bar\n", |node, map| {
189 assert_eq!(
190 getmap(&node.children[0], &map),
191 ((1, 2), (3, 6)),
192 );
193
194 assert_eq!(
195 getmap(&node.children[0].children[0], &map),
196 ((1, 2), (2, 0)),
197 );
198
199 assert_eq!(
200 getmap(&node.children[0].children[1], &map),
201 ((3, 2), (3, 6)),
202 );
203 });
204}
205
206#[test]
207fn autolinks() {
208 run("foo <http://google.com> bar", |node, map| {
209 assert_eq!(
210 getmap(&node.children[0].children[1], &map),
211 ((1, 5), (1, 23)),
212 );
213
214 assert_eq!(
215 getmap(&node.children[0].children[1].children[0], &map),
216 ((1, 6), (1, 22)),
217 );
218 });
219}
220
221#[test]
222fn emphasis() {
223 run("***foo***", |node, map| {
224 assert_eq!(
225 getmap(&node.children[0].children[0], &map),
226 ((1, 1), (1, 9)),
227 );
228
229 assert_eq!(
230 getmap(&node.children[0].children[0].children[0], &map),
231 ((1, 2), (1, 8)),
232 );
233 });
234
235 run("aaa **bb _cc_ dd** eee", |node, map| {
236 assert_eq!(
237 getmap(&node.children[0].children[1], &map),
238 ((1, 5), (1, 18)),
239 );
240
241 assert_eq!(
242 getmap(&node.children[0].children[1].children[1], &map),
243 ((1, 10), (1, 13)),
244 );
245 });
246}
247
248#[test]
249fn newline() {
250 run("foo \nbar \nbaz\nquux", |node, map| {
251 assert_eq!(
252 getmap(&node.children[0].children[1], &map),
253 ((1, 4), (2, 0)),
254 );
255
256 assert_eq!(
257 getmap(&node.children[0].children[3], &map),
258 ((2, 4), (3, 0)),
259 );
260
261 assert_eq!(
262 getmap(&node.children[0].children[5], &map),
263 ((4, 0), (4, 0)),
264 );
265
266 /*let marks : Vec<_> = node.children[0].children.iter().map(|x| getmap(x, &map)).collect();
267 assert_eq!(marks, [
268 ((1, 1), (1, 5)),
269 ((2, 0), (2, 1)),
270 ((2, 1), (2, 3)),
271 ((3, 0), (3, 0)),
272 ((3, 1), (3, 3)),
273 ]);*/
274 });
275}
276
277#[test]
278fn escapes() {
279 run("foo\\Δ\\*bar", |node, map| {
280 assert_eq!(
281 getmap(&node.children[0].children[1], &map),
282 ((1, 4), (1, 5)),
283 );
284
285 assert_eq!(
286 getmap(&node.children[0].children[2], &map),
287 ((1, 6), (1, 7)),
288 );
289 });
290
291 run(" foo \\\n bar ", |node, map| {
292 assert_eq!(
293 getmap(&node.children[0].children[1], &map),
294 ((1, 8), (2, 2)),
295 );
296 });
297}
298
299#[test]
300fn entities() {
301 run("aa &nbsp; bb &#20; cc", |node, map| {
302 assert_eq!(
303 getmap(&node.children[0].children[1], &map),
304 ((1, 4), (1, 9)),
305 );
306
307 assert_eq!(
308 getmap(&node.children[0].children[3], &map),
309 ((1, 14), (1, 18)),
310 );
311
312 /*let marks : Vec<_> = node.children[0].children.iter().map(|x| getmap(x, &map)).collect();
313 assert_eq!(marks, [
314 ((1, 1), (1, 5)),
315 ((2, 0), (2, 1)),
316 ((2, 1), (2, 3)),
317 ((3, 0), (3, 0)),
318 ((3, 1), (3, 3)),
319 ]);*/
320 });
321}
322
323#[test]
324fn html_inline() {
325 run("foo <bar> baz", |node, map| {
326 assert_eq!(
327 getmap(&node.children[0].children[1], &map),
328 ((1, 5), (1, 9)),
329 );
330 });
331}
332
333#[test]
334fn backticks() {
335 run("foo ```bar``` baz", |node, map| {
336 assert_eq!(
337 getmap(&node.children[0].children[1], &map),
338 ((1, 5), (1, 13)),
339 );
340
341 assert_eq!(
342 getmap(&node.children[0].children[1].children[0], &map),
343 ((1, 8), (1, 10)),
344 );
345 });
346
347 run("foo ` bar ` baz", |node, map| {
348 assert_eq!(
349 getmap(&node.children[0].children[1], &map),
350 ((1, 5), (1, 11)),
351 );
352
353 assert_eq!(
354 getmap(&node.children[0].children[1].children[0], &map),
355 ((1, 7), (1, 9)),
356 );
357 });
358}
359
360#[test]
361fn imglink() {
362 run("foo [bar](baz) quux", |node, map| {
363 assert_eq!(
364 getmap(&node.children[0].children[1], &map),
365 ((1, 5), (1, 14)),
366 );
367 });
368
369 run("foo ![bar](baz) quux", |node, map| {
370 assert_eq!(
371 getmap(&node.children[0].children[1], &map),
372 ((1, 5), (1, 15)),
373 );
374 });
375}
376
src/lib.rs+14
...@@ -1099,6 +1099,20 @@ mod tests {...@@ -1099,6 +1099,20 @@ mod tests {
1099 );1099 );
1100 }1100 }
11011101
1102 // Regression: EmphPairScanner panicked (subtract-with-overflow) when an
1103 // emphasis span crossed multiple blockquote continuation lines because
1104 // `token_len` was derived from srcmap file-byte offsets (inclusive of
1105 // stripped `> ` bytes) instead of inline-text offsets.
1106 #[test]
1107 fn multiline_blockquote_emphasis_does_not_panic() {
1108 let src = "> *bottom row left: the past like 4 times i've gone with akira to that\n\
1109 > pizza place, besides last time in december, they've always seated us at that\n\
1110 > exact same table. it's really cute.*";
1111 let out = run(src);
1112 assert!(out.contains("<em>"), "emphasis must be rendered");
1113 assert!(out.contains("<blockquote>"), "blockquote must be rendered");
1114 }
1115
1102 // -------------------------------------------------------------------------1116 // -------------------------------------------------------------------------
1103 // componentImports takes precedence over layout for the same element type1117 // componentImports takes precedence over layout for the same element type
1104 // -------------------------------------------------------------------------1118 // -------------------------------------------------------------------------