1pub fn effective_indent_in_blockquote(line_content: &str, expected_bq_level: usize, fallback_indent: usize) -> usize {
43 if expected_bq_level == 0 {
44 return fallback_indent;
45 }
46
47 let line_bq_level = line_content
50 .chars()
51 .take_while(|c| *c == '>' || c.is_whitespace())
52 .filter(|&c| c == '>')
53 .count();
54
55 if line_bq_level != expected_bq_level {
56 return fallback_indent;
57 }
58
59 let mut pos = 0;
61 let mut found_markers = 0;
62 for c in line_content.chars() {
63 pos += c.len_utf8();
64 if c == '>' {
65 found_markers += 1;
66 if found_markers == line_bq_level {
67 if line_content.get(pos..pos + 1) == Some(" ") {
69 pos += 1;
70 }
71 break;
72 }
73 }
74 }
75
76 let after_bq = &line_content[pos..];
77 after_bq.len() - after_bq.trim_start().len()
78}
79
80pub fn count_blockquote_level(line_content: &str) -> usize {
95 line_content
96 .chars()
97 .take_while(|c| *c == '>' || c.is_whitespace())
98 .filter(|&c| c == '>')
99 .count()
100}
101
102pub fn content_after_blockquote(line_content: &str, expected_bq_level: usize) -> &str {
118 if expected_bq_level == 0 {
119 return line_content;
120 }
121
122 let actual_level = count_blockquote_level(line_content);
124 if actual_level != expected_bq_level {
125 return line_content;
126 }
127
128 let mut pos = 0;
129 let mut found_markers = 0;
130 for c in line_content.chars() {
131 pos += c.len_utf8();
132 if c == '>' {
133 found_markers += 1;
134 if found_markers == expected_bq_level {
135 if line_content.get(pos..pos + 1) == Some(" ") {
137 pos += 1;
138 }
139 break;
140 }
141 }
142 }
143
144 &line_content[pos..]
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
156 fn test_effective_indent_no_blockquote_context() {
157 assert_eq!(effective_indent_in_blockquote("text", 0, 0), 0);
159 assert_eq!(effective_indent_in_blockquote(" text", 0, 3), 3);
160 assert_eq!(effective_indent_in_blockquote("> text", 0, 5), 5);
161 }
162
163 #[test]
164 fn test_effective_indent_single_level_blockquote() {
165 assert_eq!(effective_indent_in_blockquote("> text", 1, 99), 0);
167 assert_eq!(effective_indent_in_blockquote("> text", 1, 99), 1);
168 assert_eq!(effective_indent_in_blockquote("> text", 1, 99), 2);
169 assert_eq!(effective_indent_in_blockquote("> text", 1, 99), 3);
170 }
171
172 #[test]
173 fn test_effective_indent_no_space_after_marker() {
174 assert_eq!(effective_indent_in_blockquote(">text", 1, 99), 0);
176 assert_eq!(effective_indent_in_blockquote(">>text", 2, 99), 0);
177 }
178
179 #[test]
180 fn test_effective_indent_nested_blockquote_compact() {
181 assert_eq!(effective_indent_in_blockquote(">> text", 2, 99), 0);
183 assert_eq!(effective_indent_in_blockquote(">> text", 2, 99), 1);
184 assert_eq!(effective_indent_in_blockquote(">> text", 2, 99), 2);
185 }
186
187 #[test]
188 fn test_effective_indent_nested_blockquote_spaced() {
189 assert_eq!(effective_indent_in_blockquote("> > text", 2, 99), 0);
191 assert_eq!(effective_indent_in_blockquote("> > text", 2, 99), 1);
192 assert_eq!(effective_indent_in_blockquote("> > text", 2, 99), 2);
193 }
194
195 #[test]
196 fn test_effective_indent_mismatched_level() {
197 assert_eq!(effective_indent_in_blockquote("> text", 2, 42), 42);
199 assert_eq!(effective_indent_in_blockquote(">> text", 1, 42), 42);
200 assert_eq!(effective_indent_in_blockquote("text", 1, 42), 42);
201 }
202
203 #[test]
204 fn test_effective_indent_empty_blockquote() {
205 assert_eq!(effective_indent_in_blockquote(">", 1, 99), 0);
207 assert_eq!(effective_indent_in_blockquote("> ", 1, 99), 0);
208 assert_eq!(effective_indent_in_blockquote("> ", 1, 99), 1);
209 }
210
211 #[test]
212 fn test_effective_indent_issue_268_case() {
213 assert_eq!(effective_indent_in_blockquote("> Opening the app", 1, 0), 2);
216 assert_eq!(
217 effective_indent_in_blockquote("> [**See preview here!**](https://example.com)", 1, 0),
218 2
219 );
220 }
221
222 #[test]
223 fn test_effective_indent_triple_nested() {
224 assert_eq!(effective_indent_in_blockquote("> > > text", 3, 99), 0);
226 assert_eq!(effective_indent_in_blockquote("> > > text", 3, 99), 1);
227 assert_eq!(effective_indent_in_blockquote(">>> text", 3, 99), 0);
228 assert_eq!(effective_indent_in_blockquote(">>> text", 3, 99), 1);
229 }
230
231 #[test]
236 fn test_count_blockquote_level_none() {
237 assert_eq!(count_blockquote_level("regular text"), 0);
238 assert_eq!(count_blockquote_level(" indented text"), 0);
239 assert_eq!(count_blockquote_level(""), 0);
240 }
241
242 #[test]
243 fn test_count_blockquote_level_single() {
244 assert_eq!(count_blockquote_level("> text"), 1);
245 assert_eq!(count_blockquote_level(">text"), 1);
246 assert_eq!(count_blockquote_level(">"), 1);
247 }
248
249 #[test]
250 fn test_count_blockquote_level_nested() {
251 assert_eq!(count_blockquote_level(">> text"), 2);
252 assert_eq!(count_blockquote_level("> > text"), 2);
253 assert_eq!(count_blockquote_level(">>> text"), 3);
254 assert_eq!(count_blockquote_level("> > > text"), 3);
255 }
256
257 #[test]
262 fn test_content_after_blockquote_no_quote() {
263 assert_eq!(content_after_blockquote("text", 0), "text");
264 assert_eq!(content_after_blockquote(" indented", 0), " indented");
265 }
266
267 #[test]
268 fn test_content_after_blockquote_single() {
269 assert_eq!(content_after_blockquote("> text", 1), "text");
270 assert_eq!(content_after_blockquote(">text", 1), "text");
271 assert_eq!(content_after_blockquote("> indented", 1), " indented");
272 }
273
274 #[test]
275 fn test_content_after_blockquote_nested() {
276 assert_eq!(content_after_blockquote(">> text", 2), "text");
277 assert_eq!(content_after_blockquote("> > text", 2), "text");
278 assert_eq!(content_after_blockquote("> > indented", 2), " indented");
279 }
280
281 #[test]
282 fn test_content_after_blockquote_mismatched_level() {
283 assert_eq!(content_after_blockquote("> text", 2), "> text");
285 assert_eq!(content_after_blockquote(">> text", 1), ">> text");
286 }
287}