Markdown parser fork with extended syntax for personal use.
1//! Space or tab occurs in tons of places.
2//!
3//! ## Grammar
4//!
5//! Space or tab forms with the following BNF
6//! (<small>see [construct][crate::construct] for character groups</small>):
7//!
8//! ```bnf
9//! space_or_tab ::= 1*('\t' | ' ')
10//! ```
11//!
12//! ## References
13//!
14//! * [`micromark-factory-space/index.js` in `micromark`](https://github.com/micromark/micromark/blob/main/packages/micromark-factory-space/dev/index.js)
15
16use crate::event::{Content, Link, Name};
17use crate::state::{Name as StateName, State};
18use crate::subtokenize::link;
19use crate::tokenizer::Tokenizer;
20
21/// Configuration.
22#[derive(Debug)]
23pub struct Options {
24 /// Minimum allowed bytes (inclusive).
25 pub min: usize,
26 /// Maximum allowed bytes (inclusive).
27 pub max: usize,
28 /// Name to use for events.
29 pub kind: Name,
30 /// Connect this event to the previous.
31 pub connect: bool,
32 /// Embedded content type to use.
33 pub content: Option<Content>,
34}
35
36/// One or more `space_or_tab`.
37///
38/// ```bnf
39/// space_or_tab ::= 1*( ' ' '\t' )
40/// ```
41pub fn space_or_tab(tokenizer: &mut Tokenizer) -> StateName {
42 space_or_tab_min_max(tokenizer, 1, usize::MAX)
43}
44
45/// Between `x` and `y` `space_or_tab`.
46///
47/// ```bnf
48/// space_or_tab_min_max ::= x*y( ' ' '\t' )
49/// ```
50pub fn space_or_tab_min_max(tokenizer: &mut Tokenizer, min: usize, max: usize) -> StateName {
51 space_or_tab_with_options(
52 tokenizer,
53 Options {
54 kind: Name::SpaceOrTab,
55 min,
56 max,
57 content: None,
58 connect: false,
59 },
60 )
61}
62
63/// `space_or_tab`, with the given options.
64pub fn space_or_tab_with_options(tokenizer: &mut Tokenizer, options: Options) -> StateName {
65 tokenizer.tokenize_state.space_or_tab_connect = options.connect;
66 tokenizer.tokenize_state.space_or_tab_content = options.content;
67 tokenizer.tokenize_state.space_or_tab_min = options.min;
68 tokenizer.tokenize_state.space_or_tab_max = options.max;
69 tokenizer.tokenize_state.space_or_tab_token = options.kind;
70 StateName::SpaceOrTabStart
71}
72
73/// Start of `space_or_tab`.
74///
75/// ```markdown
76/// > | a␠␠b
77/// ^
78/// ```
79pub fn start(tokenizer: &mut Tokenizer) -> State {
80 if tokenizer.tokenize_state.space_or_tab_max > 0
81 && matches!(tokenizer.current, Some(b'\t' | b' '))
82 {
83 if let Some(ref content) = tokenizer.tokenize_state.space_or_tab_content {
84 tokenizer.enter_link(
85 tokenizer.tokenize_state.space_or_tab_token.clone(),
86 Link {
87 previous: None,
88 next: None,
89 content: content.clone(),
90 },
91 );
92 } else {
93 tokenizer.enter(tokenizer.tokenize_state.space_or_tab_token.clone());
94 }
95
96 if tokenizer.tokenize_state.space_or_tab_connect {
97 let index = tokenizer.events.len() - 1;
98 link(&mut tokenizer.events, index);
99 } else {
100 tokenizer.tokenize_state.space_or_tab_connect = true;
101 }
102
103 State::Retry(StateName::SpaceOrTabInside)
104 } else {
105 State::Retry(StateName::SpaceOrTabAfter)
106 }
107}
108
109/// In `space_or_tab`.
110///
111/// ```markdown
112/// > | a␠␠b
113/// ^
114/// ```
115pub fn inside(tokenizer: &mut Tokenizer) -> State {
116 match tokenizer.current {
117 Some(b'\t' | b' ')
118 if tokenizer.tokenize_state.space_or_tab_size
119 < tokenizer.tokenize_state.space_or_tab_max =>
120 {
121 tokenizer.consume();
122 tokenizer.tokenize_state.space_or_tab_size += 1;
123 State::Next(StateName::SpaceOrTabInside)
124 }
125 _ => {
126 tokenizer.exit(tokenizer.tokenize_state.space_or_tab_token.clone());
127 State::Retry(StateName::SpaceOrTabAfter)
128 }
129 }
130}
131
132/// After `space_or_tab`.
133///
134/// ```markdown
135/// > | a␠␠b
136/// ^
137/// ```
138pub fn after(tokenizer: &mut Tokenizer) -> State {
139 let state = if tokenizer.tokenize_state.space_or_tab_size
140 >= tokenizer.tokenize_state.space_or_tab_min
141 {
142 State::Ok
143 } else {
144 State::Nok
145 };
146 tokenizer.tokenize_state.space_or_tab_connect = false;
147 tokenizer.tokenize_state.space_or_tab_content = None;
148 tokenizer.tokenize_state.space_or_tab_size = 0;
149 tokenizer.tokenize_state.space_or_tab_max = 0;
150 tokenizer.tokenize_state.space_or_tab_min = 0;
151 tokenizer.tokenize_state.space_or_tab_token = Name::SpaceOrTab;
152 state
153}