1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
//! Block quote is a construct that occurs in the [document][] content type.
//!
//! It forms with the following BNF:
//!
//! ```bnf
//! block_quote_start ::= '>' [ space_or_tab ]
//! block_quote_cont ::= '>' [ space_or_tab ]
//! ```
//!
//! Further lines that are not prefixed with `block_quote_cont` cause the block
//! quote to be exited, except when those lines are lazy continuation.
//! Like so many things in markdown, block quotes too, are very complex.
//! See [*§ Phase 1: block structure*][commonmark-block] for more on parsing
//! details.
//!
//! Block quote relates to the `<blockquote>` element in HTML.
//! See [*§ 4.4.4 The `blockquote` element*][html-blockquote] in the HTML spec
//! for more info.
//!
//! ## Tokens
//!
//! * [`BlockQuote`][Token::BlockQuote]
//! * [`BlockQuoteMarker`][Token::BlockQuoteMarker]
//! * [`BlockQuotePrefix`][Token::BlockQuotePrefix]
//! * [`SpaceOrTab`][Token::SpaceOrTab]
//!
//! ## References
//!
//! * [`block-quote.js` in `micromark`](https://github.com/micromark/micromark/blob/main/packages/micromark-core-commonmark/dev/lib/block-quote.js)
//! * [*§ 5.1 Block quotes* in `CommonMark`](https://spec.commonmark.org/0.30/#block-quotes)
//!
//! [document]: crate::content::document
//! [html-blockquote]: https://html.spec.whatwg.org/multipage/grouping-content.html#the-blockquote-element
//! [commonmark-block]: https://spec.commonmark.org/0.30/#phase-1-block-structure
use crate::constant::TAB_SIZE;
use crate::construct::partial_space_or_tab::space_or_tab_min_max;
use crate::token::Token;
use crate::tokenizer::{Code, State, Tokenizer};
/// Start of block quote.
///
/// ```markdown
/// > | > a
/// ^
/// ```
pub fn start(tokenizer: &mut Tokenizer) -> State {
let max = if tokenizer.parse_state.constructs.code_indented {
TAB_SIZE - 1
} else {
usize::MAX
};
if tokenizer.parse_state.constructs.block_quote {
tokenizer.go(space_or_tab_min_max(0, max), before)(tokenizer)
} else {
State::Nok
}
}
/// Start of block quote, after whitespace, before `>`.
///
/// ```markdown
/// > | > a
/// ^
/// ```
fn before(tokenizer: &mut Tokenizer) -> State {
match tokenizer.current {
Code::Char('>') => {
tokenizer.enter(Token::BlockQuote);
cont_before(tokenizer)
}
_ => cont_before(tokenizer),
}
}
/// Start of block quote continuation.
///
/// ```markdown
/// | > a
/// > | > b
/// ^
/// ```
pub fn cont(tokenizer: &mut Tokenizer) -> State {
let max = if tokenizer.parse_state.constructs.code_indented {
TAB_SIZE - 1
} else {
usize::MAX
};
tokenizer.go(space_or_tab_min_max(0, max), cont_before)(tokenizer)
}
/// After whitespace, before `>`.
///
/// ```markdown
/// | > a
/// > | > b
/// ^
/// ```
fn cont_before(tokenizer: &mut Tokenizer) -> State {
match tokenizer.current {
Code::Char('>') => {
tokenizer.enter(Token::BlockQuotePrefix);
tokenizer.enter(Token::BlockQuoteMarker);
tokenizer.consume();
tokenizer.exit(Token::BlockQuoteMarker);
State::Fn(Box::new(cont_after))
}
_ => State::Nok,
}
}
/// After `>`, before optional whitespace.
///
/// ```markdown
/// > | > a
/// ^
/// > | >b
/// ^
/// ```
fn cont_after(tokenizer: &mut Tokenizer) -> State {
match tokenizer.current {
Code::VirtualSpace | Code::Char('\t' | ' ') => {
tokenizer.enter(Token::SpaceOrTab);
tokenizer.consume();
tokenizer.exit(Token::SpaceOrTab);
tokenizer.exit(Token::BlockQuotePrefix);
State::Ok
}
_ => {
tokenizer.exit(Token::BlockQuotePrefix);
State::Ok
}
}
}
|