1mod autolink;
2mod inlines;
3#[cfg(feature = "shortcodes")]
4pub mod shortcodes;
5mod table;
6
7pub mod math;
8pub mod multiline_block_quote;
9
10use crate::adapters::SyntaxHighlighterAdapter;
11use crate::arena_tree::Node;
12use crate::ctype::{isdigit, isspace};
13use crate::entity;
14use crate::nodes::{self, NodeFootnoteDefinition, Sourcepos};
15use crate::nodes::{
16 Ast, AstNode, ListDelimType, ListType, NodeCodeBlock, NodeDescriptionItem, NodeHeading,
17 NodeHtmlBlock, NodeList, NodeValue,
18};
19use crate::scanners::{self, SetextChar};
20use crate::strings::{self, split_off_front_matter, Case};
21use bon::Builder;
22use std::cell::RefCell;
23use std::cmp::min;
24use std::collections::HashMap;
25use std::fmt::{self, Debug, Formatter};
26use std::mem;
27use std::panic::RefUnwindSafe;
28use std::str;
29use std::sync::Arc;
30use typed_arena::Arena;
31
32use crate::adapters::HeadingAdapter;
33use crate::parser::multiline_block_quote::NodeMultilineBlockQuote;
34
35use self::inlines::RefMap;
36
37const TAB_STOP: usize = 4;
38const CODE_INDENT: usize = 4;
39
40const MAX_LIST_DEPTH: usize = 100;
45
46macro_rules! node_matches {
47 ($node:expr, $( $pat:pat )|+) => {{
48 matches!(
49 $node.data.borrow().value,
50 $( $pat )|+
51 )
52 }};
53}
54
55pub fn parse_document<'a>(
59 arena: &'a Arena<AstNode<'a>>,
60 buffer: &str,
61 options: &Options,
62) -> &'a AstNode<'a> {
63 let root: &'a AstNode<'a> = arena.alloc(Node::new(RefCell::new(Ast {
64 value: NodeValue::Document,
65 content: String::new(),
66 sourcepos: (1, 1, 1, 1).into(),
67 internal_offset: 0,
68 open: true,
69 last_line_blank: false,
70 table_visited: false,
71 line_offsets: Vec::with_capacity(0),
72 })));
73 let mut parser = Parser::new(arena, root, options);
74 let mut linebuf = Vec::with_capacity(buffer.len());
75 parser.feed(&mut linebuf, buffer, true);
76 parser.finish(linebuf)
77}
78
79#[deprecated(
82 since = "0.25.0",
83 note = "The broken link callback has been moved into ParseOptions."
84)]
85pub fn parse_document_with_broken_link_callback<'a, 'c>(
86 arena: &'a Arena<AstNode<'a>>,
87 buffer: &str,
88 options: &Options,
89 callback: Arc<dyn BrokenLinkCallback + 'c>,
90) -> &'a AstNode<'a> {
91 let mut options_with_callback = options.clone();
92 options_with_callback.parse.broken_link_callback = Some(callback);
93 parse_document(arena, buffer, &options_with_callback)
94}
95
96pub trait BrokenLinkCallback: RefUnwindSafe + Send + Sync {
104 fn resolve(&self, broken_link_reference: BrokenLinkReference) -> Option<ResolvedReference>;
106}
107
108impl<'c> Debug for dyn BrokenLinkCallback + 'c {
109 fn fmt(&self, formatter: &mut Formatter<'_>) -> Result<(), fmt::Error> {
110 formatter.write_str("<dyn BrokenLinkCallback>")
111 }
112}
113
114impl<F> BrokenLinkCallback for F
115where
116 F: Fn(BrokenLinkReference) -> Option<ResolvedReference>,
117 F: RefUnwindSafe + Send + Sync,
118{
119 fn resolve(&self, broken_link_reference: BrokenLinkReference) -> Option<ResolvedReference> {
120 self(broken_link_reference)
121 }
122}
123
124#[derive(Debug)]
127pub struct BrokenLinkReference<'l> {
128 pub normalized: &'l str,
132
133 pub original: &'l str,
135}
136
137pub struct Parser<'a, 'o, 'c> {
138 arena: &'a Arena<AstNode<'a>>,
139 refmap: RefMap,
140 root: &'a AstNode<'a>,
141 current: &'a AstNode<'a>,
142 line_number: usize,
143 offset: usize,
144 column: usize,
145 thematic_break_kill_pos: usize,
146 first_nonspace: usize,
147 first_nonspace_column: usize,
148 indent: usize,
149 blank: bool,
150 partially_consumed_tab: bool,
151 curline_len: usize,
152 curline_end_col: usize,
153 last_line_length: usize,
154 last_buffer_ended_with_cr: bool,
155 total_size: usize,
156 options: &'o Options<'c>,
157}
158
159#[derive(Default, Debug, Clone)]
160#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
161pub struct Options<'c> {
163 pub extension: ExtensionOptions<'c>,
165
166 pub parse: ParseOptions<'c>,
168
169 pub render: RenderOptions,
171}
172
173pub trait URLRewriter: RefUnwindSafe + Send + Sync {
175 fn to_html(&self, url: &str) -> String;
177}
178
179impl<'c> Debug for dyn URLRewriter + 'c {
180 fn fmt(&self, formatter: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
181 formatter.write_str("<dyn URLRewriter>")
182 }
183}
184
185impl<F> URLRewriter for F
186where
187 F: for<'a> Fn(&'a str) -> String,
188 F: RefUnwindSafe + Send + Sync,
189{
190 fn to_html(&self, url: &str) -> String {
191 self(url)
192 }
193}
194
195#[non_exhaustive]
196#[derive(Debug, Clone, PartialEq, Eq, Copy)]
197#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
198pub(crate) enum WikiLinksMode {
200 UrlFirst,
203
204 TitleFirst,
206}
207
208#[non_exhaustive]
209#[derive(Default, Debug, Clone, Builder)]
210#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
211pub struct ExtensionOptions<'c> {
213 #[builder(default)]
225 pub strikethrough: bool,
226
227 #[builder(default)]
240 pub tagfilter: bool,
241
242 #[builder(default)]
254 pub table: bool,
255
256 #[builder(default)]
267 pub autolink: bool,
268
269 #[builder(default)]
286 pub tasklist: bool,
287
288 #[builder(default)]
298 pub superscript: bool,
299
300 pub header_ids: Option<String>,
310
311 #[builder(default)]
324 pub footnotes: bool,
325
326 #[builder(default)]
353 pub description_lists: bool,
354
355 pub front_matter_delimiter: Option<String>,
394
395 #[builder(default)]
419 pub multiline_block_quotes: bool,
420
421 #[builder(default)]
441 pub math_dollars: bool,
442
443 #[builder(default)]
463 pub math_code: bool,
464
465 #[cfg(feature = "shortcodes")]
466 #[cfg_attr(docsrs, doc(cfg(feature = "shortcodes")))]
467 #[builder(default)]
480 pub shortcodes: bool,
481
482 #[builder(default)]
501 pub wikilinks_title_after_pipe: bool,
502
503 #[builder(default)]
521 pub wikilinks_title_before_pipe: bool,
522
523 #[builder(default)]
538 pub underline: bool,
539
540 #[builder(default)]
558 pub subscript: bool,
559
560 #[builder(default)]
575 pub spoiler: bool,
576
577 #[builder(default)]
605 pub greentext: bool,
606
607 #[cfg_attr(feature = "arbitrary", arbitrary(value = None))]
622 pub image_url_rewriter: Option<Arc<dyn URLRewriter + 'c>>,
623
624 #[cfg_attr(feature = "arbitrary", arbitrary(value = None))]
639 pub link_url_rewriter: Option<Arc<dyn URLRewriter + 'c>>,
640}
641
642impl<'c> ExtensionOptions<'c> {
643 pub(crate) fn wikilinks(&self) -> Option<WikiLinksMode> {
644 match (
645 self.wikilinks_title_before_pipe,
646 self.wikilinks_title_after_pipe,
647 ) {
648 (false, false) => None,
649 (true, false) => Some(WikiLinksMode::TitleFirst),
650 (_, _) => Some(WikiLinksMode::UrlFirst),
651 }
652 }
653}
654
655#[non_exhaustive]
656#[derive(Default, Clone, Debug, Builder)]
657#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
658pub struct ParseOptions<'c> {
660 #[builder(default)]
673 pub smart: bool,
674
675 pub default_info_string: Option<String>,
688
689 #[builder(default)]
691 pub relaxed_tasklist_matching: bool,
692
693 #[builder(default)]
709 pub relaxed_autolinks: bool,
710
711 #[cfg_attr(feature = "arbitrary", arbitrary(default))]
741 pub broken_link_callback: Option<Arc<dyn BrokenLinkCallback + 'c>>,
742}
743
744#[non_exhaustive]
745#[derive(Default, Debug, Clone, Copy, Builder)]
746#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
747pub struct RenderOptions {
749 #[builder(default)]
763 pub hardbreaks: bool,
764
765 #[builder(default)]
778 pub github_pre_lang: bool,
779
780 #[builder(default)]
794 pub full_info_string: bool,
795
796 #[builder(default)]
817 pub width: usize,
818
819 #[builder(default)]
843 pub unsafe_: bool,
844
845 #[builder(default)]
859 pub escape: bool,
860
861 #[builder(default)]
883 pub list_style: ListStyleType,
884
885 #[builder(default)]
906 pub sourcepos: bool,
907
908 #[builder(default)]
922 pub experimental_inline_sourcepos: bool,
923
924 #[builder(default)]
940 pub escaped_char_spans: bool,
941
942 #[builder(default)]
957 pub ignore_setext: bool,
958
959 #[builder(default)]
973 pub ignore_empty_links: bool,
974
975 #[builder(default)]
990 pub gfm_quirks: bool,
991
992 #[builder(default)]
1012 pub prefer_fenced: bool,
1013
1014 #[builder(default)]
1029 pub figure_with_caption: bool,
1030
1031 #[builder(default)]
1047 pub tasklist_classes: bool,
1048
1049 #[builder(default)]
1065 pub ol_width: usize,
1066}
1067
1068#[non_exhaustive]
1069#[derive(Default, Debug, Clone, Builder)]
1070pub struct Plugins<'p> {
1072 #[builder(default)]
1074 pub render: RenderPlugins<'p>,
1075}
1076
1077#[non_exhaustive]
1078#[derive(Default, Clone, Builder)]
1079pub struct RenderPlugins<'p> {
1081 pub codefence_syntax_highlighter: Option<&'p dyn SyntaxHighlighterAdapter>,
1117
1118 pub heading_adapter: Option<&'p dyn HeadingAdapter>,
1120}
1121
1122impl Debug for RenderPlugins<'_> {
1123 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1124 f.debug_struct("RenderPlugins")
1125 .field(
1126 "codefence_syntax_highlighter",
1127 &"impl SyntaxHighlighterAdapter",
1128 )
1129 .finish()
1130 }
1131}
1132
1133#[derive(Clone, Debug)]
1135pub struct ResolvedReference {
1136 pub url: String,
1138
1139 pub title: String,
1141}
1142
1143struct FootnoteDefinition<'a> {
1144 ix: Option<u32>,
1145 node: &'a AstNode<'a>,
1146 name: String,
1147 total_references: u32,
1148}
1149
1150impl<'a, 'o, 'c> Parser<'a, 'o, 'c>
1151where
1152 'c: 'o,
1153{
1154 fn new(arena: &'a Arena<AstNode<'a>>, root: &'a AstNode<'a>, options: &'o Options<'c>) -> Self {
1155 Parser {
1156 arena,
1157 refmap: RefMap::new(),
1158 root,
1159 current: root,
1160 line_number: 0,
1161 offset: 0,
1162 column: 0,
1163 thematic_break_kill_pos: 0,
1164 first_nonspace: 0,
1165 first_nonspace_column: 0,
1166 indent: 0,
1167 blank: false,
1168 partially_consumed_tab: false,
1169 curline_len: 0,
1170 curline_end_col: 0,
1171 last_line_length: 0,
1172 last_buffer_ended_with_cr: false,
1173 total_size: 0,
1174 options,
1175 }
1176 }
1177
1178 fn feed(&mut self, linebuf: &mut Vec<u8>, mut s: &str, eof: bool) {
1179 if let (0, Some(delimiter)) = (
1180 self.total_size,
1181 &self.options.extension.front_matter_delimiter,
1182 ) {
1183 if let Some((front_matter, rest)) = split_off_front_matter(s, delimiter) {
1184 let lines = front_matter
1185 .as_bytes()
1186 .iter()
1187 .filter(|b| **b == b'\n')
1188 .count();
1189
1190 let mut stripped_front_matter = front_matter.to_string();
1191 strings::remove_trailing_blank_lines(&mut stripped_front_matter);
1192 let stripped_lines = stripped_front_matter
1193 .as_bytes()
1194 .iter()
1195 .filter(|b| **b == b'\n')
1196 .count();
1197
1198 let node = self.add_child(
1199 self.root,
1200 NodeValue::FrontMatter(front_matter.to_string()),
1201 1,
1202 );
1203 s = rest;
1204 self.finalize(node).unwrap();
1205
1206 node.data.borrow_mut().sourcepos = Sourcepos {
1207 start: nodes::LineColumn { line: 1, column: 1 },
1208 end: nodes::LineColumn {
1209 line: 1 + stripped_lines,
1210 column: delimiter.len(),
1211 },
1212 };
1213 self.line_number += lines;
1214 }
1215 }
1216
1217 let s = s.as_bytes();
1218
1219 if s.len() > usize::MAX - self.total_size {
1220 self.total_size = usize::MAX;
1221 } else {
1222 self.total_size += s.len();
1223 }
1224
1225 let mut buffer = 0;
1226 if self.last_buffer_ended_with_cr && !s.is_empty() && s[0] == b'\n' {
1227 buffer += 1;
1228 }
1229 self.last_buffer_ended_with_cr = false;
1230
1231 let end = s.len();
1232
1233 while buffer < end {
1234 let mut process = false;
1235 let mut eol = buffer;
1236 while eol < end {
1237 if strings::is_line_end_char(s[eol]) {
1238 process = true;
1239 break;
1240 }
1241 if s[eol] == 0 {
1242 break;
1243 }
1244 eol += 1;
1245 }
1246
1247 if eol >= end && eof {
1248 process = true;
1249 }
1250
1251 if process {
1252 if !linebuf.is_empty() {
1253 linebuf.extend_from_slice(&s[buffer..eol]);
1254 self.process_line(linebuf);
1255 linebuf.truncate(0);
1256 } else {
1257 self.process_line(&s[buffer..eol]);
1258 }
1259 } else if eol < end && s[eol] == b'\0' {
1260 linebuf.extend_from_slice(&s[buffer..eol]);
1261 linebuf.extend_from_slice(&"\u{fffd}".to_string().into_bytes());
1262 } else {
1263 linebuf.extend_from_slice(&s[buffer..eol]);
1264 }
1265
1266 buffer = eol;
1267 if buffer < end {
1268 if s[buffer] == b'\0' {
1269 buffer += 1;
1270 } else {
1271 if s[buffer] == b'\r' {
1272 buffer += 1;
1273 if buffer == end {
1274 self.last_buffer_ended_with_cr = true;
1275 }
1276 }
1277 if buffer < end && s[buffer] == b'\n' {
1278 buffer += 1;
1279 }
1280 }
1281 }
1282 }
1283 }
1284
1285 fn scan_thematic_break_inner(&mut self, line: &[u8]) -> (usize, bool) {
1286 let mut i = self.first_nonspace;
1287
1288 if i >= line.len() {
1289 return (i, false);
1290 }
1291
1292 let c = line[i];
1293 if c != b'*' && c != b'_' && c != b'-' {
1294 return (i, false);
1295 }
1296
1297 let mut count = 1;
1298 let mut nextc;
1299 loop {
1300 i += 1;
1301 if i >= line.len() {
1302 return (i, false);
1303 }
1304 nextc = line[i];
1305
1306 if nextc == c {
1307 count += 1;
1308 } else if nextc != b' ' && nextc != b'\t' {
1309 break;
1310 }
1311 }
1312
1313 if count >= 3 && (nextc == b'\r' || nextc == b'\n') {
1314 ((i - self.first_nonspace) + 1, true)
1315 } else {
1316 (i, false)
1317 }
1318 }
1319
1320 fn scan_thematic_break(&mut self, line: &[u8]) -> Option<usize> {
1321 let (offset, found) = self.scan_thematic_break_inner(line);
1322 if !found {
1323 self.thematic_break_kill_pos = offset;
1324 None
1325 } else {
1326 Some(offset)
1327 }
1328 }
1329
1330 fn find_first_nonspace(&mut self, line: &[u8]) {
1331 let mut chars_to_tab = TAB_STOP - (self.column % TAB_STOP);
1332
1333 if self.first_nonspace <= self.offset {
1334 self.first_nonspace = self.offset;
1335 self.first_nonspace_column = self.column;
1336
1337 loop {
1338 if self.first_nonspace >= line.len() {
1339 break;
1340 }
1341 match line[self.first_nonspace] {
1342 32 => {
1343 self.first_nonspace += 1;
1344 self.first_nonspace_column += 1;
1345 chars_to_tab -= 1;
1346 if chars_to_tab == 0 {
1347 chars_to_tab = TAB_STOP;
1348 }
1349 }
1350 9 => {
1351 self.first_nonspace += 1;
1352 self.first_nonspace_column += chars_to_tab;
1353 chars_to_tab = TAB_STOP;
1354 }
1355 _ => break,
1356 }
1357 }
1358 }
1359
1360 self.indent = self.first_nonspace_column - self.column;
1361 self.blank = self.first_nonspace < line.len()
1362 && strings::is_line_end_char(line[self.first_nonspace]);
1363 }
1364
1365 fn process_line(&mut self, line: &[u8]) {
1366 let mut new_line: Vec<u8>;
1367 let line = if line.is_empty() || !strings::is_line_end_char(*line.last().unwrap()) {
1368 new_line = line.into();
1369 new_line.push(b'\n');
1370 &new_line
1371 } else {
1372 line
1373 };
1374
1375 self.curline_len = line.len();
1376 self.curline_end_col = line.len();
1377 if self.curline_end_col > 0 && line[self.curline_end_col - 1] == b'\n' {
1378 self.curline_end_col -= 1;
1379 }
1380 if self.curline_end_col > 0 && line[self.curline_end_col - 1] == b'\r' {
1381 self.curline_end_col -= 1;
1382 }
1383
1384 self.offset = 0;
1385 self.column = 0;
1386 self.first_nonspace = 0;
1387 self.first_nonspace_column = 0;
1388 self.indent = 0;
1389 self.thematic_break_kill_pos = 0;
1390 self.blank = false;
1391 self.partially_consumed_tab = false;
1392
1393 if self.line_number == 0
1394 && line.len() >= 3
1395 && unsafe { str::from_utf8_unchecked(line) }.starts_with('\u{feff}')
1396 {
1397 self.offset += 3;
1398 }
1399
1400 self.line_number += 1;
1401
1402 let mut all_matched = true;
1403 if let Some(last_matched_container) = self.check_open_blocks(line, &mut all_matched) {
1404 let mut container = last_matched_container;
1405 let current = self.current;
1406 self.open_new_blocks(&mut container, line, all_matched);
1407
1408 if current.same_node(self.current) {
1409 self.add_text_to_container(container, last_matched_container, line);
1410 }
1411 }
1412
1413 self.last_line_length = self.curline_end_col;
1414
1415 self.curline_len = 0;
1416 self.curline_end_col = 0;
1417 }
1418
1419 fn check_open_blocks(
1420 &mut self,
1421 line: &[u8],
1422 all_matched: &mut bool,
1423 ) -> Option<&'a AstNode<'a>> {
1424 let (new_all_matched, mut container, should_continue) =
1425 self.check_open_blocks_inner(self.root, line);
1426
1427 *all_matched = new_all_matched;
1428 if !*all_matched {
1429 container = container.parent().unwrap();
1430 }
1431
1432 if !should_continue {
1433 None
1434 } else {
1435 Some(container)
1436 }
1437 }
1438
1439 fn check_open_blocks_inner(
1440 &mut self,
1441 mut container: &'a AstNode<'a>,
1442 line: &[u8],
1443 ) -> (bool, &'a AstNode<'a>, bool) {
1444 let mut should_continue = true;
1445
1446 while nodes::last_child_is_open(container) {
1447 container = container.last_child().unwrap();
1448 let ast = &mut *container.data.borrow_mut();
1449
1450 self.find_first_nonspace(line);
1451
1452 match ast.value {
1453 NodeValue::BlockQuote => {
1454 if !self.parse_block_quote_prefix(line) {
1455 return (false, container, should_continue);
1456 }
1457 }
1458 NodeValue::Item(ref nl) => {
1459 if !self.parse_node_item_prefix(line, container, nl) {
1460 return (false, container, should_continue);
1461 }
1462 }
1463 NodeValue::DescriptionItem(ref di) => {
1464 if !self.parse_description_item_prefix(line, container, di) {
1465 return (false, container, should_continue);
1466 }
1467 }
1468 NodeValue::CodeBlock(..) => {
1469 if !self.parse_code_block_prefix(line, container, ast, &mut should_continue) {
1470 return (false, container, should_continue);
1471 }
1472 }
1473 NodeValue::HtmlBlock(ref nhb) => {
1474 if !self.parse_html_block_prefix(nhb.block_type) {
1475 return (false, container, should_continue);
1476 }
1477 }
1478 NodeValue::Paragraph => {
1479 if self.blank {
1480 return (false, container, should_continue);
1481 }
1482 }
1483 NodeValue::Table(..) => {
1484 if !table::matches(&line[self.first_nonspace..], self.options.extension.spoiler)
1485 {
1486 return (false, container, should_continue);
1487 }
1488 continue;
1489 }
1490 NodeValue::Heading(..) | NodeValue::TableRow(..) | NodeValue::TableCell => {
1491 return (false, container, should_continue);
1492 }
1493 NodeValue::FootnoteDefinition(..) => {
1494 if !self.parse_footnote_definition_block_prefix(line) {
1495 return (false, container, should_continue);
1496 }
1497 }
1498 NodeValue::MultilineBlockQuote(..) => {
1499 if !self.parse_multiline_block_quote_prefix(
1500 line,
1501 container,
1502 ast,
1503 &mut should_continue,
1504 ) {
1505 return (false, container, should_continue);
1506 }
1507 }
1508 _ => {}
1509 }
1510 }
1511
1512 (true, container, should_continue)
1513 }
1514
1515 fn is_not_greentext(&mut self, line: &[u8]) -> bool {
1516 !self.options.extension.greentext || strings::is_space_or_tab(line[self.first_nonspace + 1])
1517 }
1518
1519 fn setext_heading_line(&mut self, s: &[u8]) -> Option<SetextChar> {
1520 match self.options.render.ignore_setext {
1521 false => scanners::setext_heading_line(s),
1522 true => None,
1523 }
1524 }
1525
1526 fn detect_multiline_blockquote(
1527 &mut self,
1528 line: &[u8],
1529 indented: bool,
1530 matched: &mut usize,
1531 ) -> bool {
1532 !indented
1533 && self.options.extension.multiline_block_quotes
1534 && unwrap_into(
1535 scanners::open_multiline_block_quote_fence(&line[self.first_nonspace..]),
1536 matched,
1537 )
1538 }
1539
1540 fn handle_multiline_blockquote(
1541 &mut self,
1542 container: &mut &'a Node<'a, RefCell<Ast>>,
1543 line: &[u8],
1544 indented: bool,
1545 matched: &mut usize,
1546 ) -> bool {
1547 if !self.detect_multiline_blockquote(line, indented, matched) {
1548 return false;
1549 }
1550
1551 let first_nonspace = self.first_nonspace;
1552 let offset = self.offset;
1553 let nmbc = NodeMultilineBlockQuote {
1554 fence_length: *matched,
1555 fence_offset: first_nonspace - offset,
1556 };
1557
1558 *container = self.add_child(
1559 container,
1560 NodeValue::MultilineBlockQuote(nmbc),
1561 self.first_nonspace + 1,
1562 );
1563
1564 self.advance_offset(line, first_nonspace + *matched - offset, false);
1565
1566 true
1567 }
1568
1569 fn detect_blockquote(&mut self, line: &[u8], indented: bool) -> bool {
1570 !indented && line[self.first_nonspace] == b'>' && self.is_not_greentext(line)
1571 }
1572
1573 fn handle_blockquote(
1574 &mut self,
1575 container: &mut &'a Node<'a, RefCell<Ast>>,
1576 line: &[u8],
1577 indented: bool,
1578 ) -> bool {
1579 if !self.detect_blockquote(line, indented) {
1580 return false;
1581 }
1582
1583 let blockquote_startpos = self.first_nonspace;
1584
1585 let offset = self.first_nonspace + 1 - self.offset;
1586 self.advance_offset(line, offset, false);
1587 if strings::is_space_or_tab(line[self.offset]) {
1588 self.advance_offset(line, 1, true);
1589 }
1590 *container = self.add_child(container, NodeValue::BlockQuote, blockquote_startpos + 1);
1591
1592 true
1593 }
1594
1595 fn detect_atx_heading(&mut self, line: &[u8], indented: bool, matched: &mut usize) -> bool {
1596 !indented
1597 && unwrap_into(
1598 scanners::atx_heading_start(&line[self.first_nonspace..]),
1599 matched,
1600 )
1601 }
1602
1603 fn handle_atx_heading(
1604 &mut self,
1605 container: &mut &'a Node<'a, RefCell<Ast>>,
1606 line: &[u8],
1607 indented: bool,
1608 matched: &mut usize,
1609 ) -> bool {
1610 if !self.detect_atx_heading(line, indented, matched) {
1611 return false;
1612 }
1613
1614 let heading_startpos = self.first_nonspace;
1615 let offset = self.offset;
1616 self.advance_offset(line, heading_startpos + *matched - offset, false);
1617 *container = self.add_child(
1618 container,
1619 NodeValue::Heading(NodeHeading::default()),
1620 heading_startpos + 1,
1621 );
1622
1623 let mut hashpos = line[self.first_nonspace..]
1624 .iter()
1625 .position(|&c| c == b'#')
1626 .unwrap()
1627 + self.first_nonspace;
1628 let mut level = 0;
1629 while line[hashpos] == b'#' {
1630 level += 1;
1631 hashpos += 1;
1632 }
1633
1634 let container_ast = &mut container.data.borrow_mut();
1635 container_ast.value = NodeValue::Heading(NodeHeading {
1636 level,
1637 setext: false,
1638 });
1639 container_ast.internal_offset = *matched;
1640
1641 true
1642 }
1643
1644 fn detect_code_fence(&mut self, line: &[u8], indented: bool, matched: &mut usize) -> bool {
1645 !indented
1646 && unwrap_into(
1647 scanners::open_code_fence(&line[self.first_nonspace..]),
1648 matched,
1649 )
1650 }
1651
1652 fn handle_code_fence(
1653 &mut self,
1654 container: &mut &'a Node<'a, RefCell<Ast>>,
1655 line: &[u8],
1656 indented: bool,
1657 matched: &mut usize,
1658 ) -> bool {
1659 if !self.detect_code_fence(line, indented, matched) {
1660 return false;
1661 }
1662
1663 let first_nonspace = self.first_nonspace;
1664 let offset = self.offset;
1665 let ncb = NodeCodeBlock {
1666 fenced: true,
1667 fence_char: line[first_nonspace],
1668 fence_length: *matched,
1669 fence_offset: first_nonspace - offset,
1670 info: String::with_capacity(10),
1671 literal: String::new(),
1672 };
1673 *container = self.add_child(
1674 container,
1675 NodeValue::CodeBlock(ncb),
1676 self.first_nonspace + 1,
1677 );
1678 self.advance_offset(line, first_nonspace + *matched - offset, false);
1679
1680 true
1681 }
1682
1683 fn detect_html_block(
1684 &mut self,
1685 container: &AstNode,
1686 line: &[u8],
1687 indented: bool,
1688 matched: &mut usize,
1689 ) -> bool {
1690 !indented
1691 && (unwrap_into(
1692 scanners::html_block_start(&line[self.first_nonspace..]),
1693 matched,
1694 ) || (!node_matches!(container, NodeValue::Paragraph)
1695 && unwrap_into(
1696 scanners::html_block_start_7(&line[self.first_nonspace..]),
1697 matched,
1698 )))
1699 }
1700
1701 fn handle_html_block(
1702 &mut self,
1703 container: &mut &'a Node<'a, RefCell<Ast>>,
1704 line: &[u8],
1705 indented: bool,
1706 matched: &mut usize,
1707 ) -> bool {
1708 if !self.detect_html_block(container, line, indented, matched) {
1709 return false;
1710 }
1711
1712 let nhb = NodeHtmlBlock {
1713 block_type: *matched as u8,
1714 literal: String::new(),
1715 };
1716
1717 *container = self.add_child(
1718 container,
1719 NodeValue::HtmlBlock(nhb),
1720 self.first_nonspace + 1,
1721 );
1722
1723 true
1724 }
1725
1726 fn detect_setext_heading(
1727 &mut self,
1728 container: &AstNode,
1729 line: &[u8],
1730 indented: bool,
1731 sc: &mut scanners::SetextChar,
1732 ) -> bool {
1733 !indented
1734 && node_matches!(container, NodeValue::Paragraph)
1735 && unwrap_into(self.setext_heading_line(&line[self.first_nonspace..]), sc)
1736 }
1737
1738 fn handle_setext_heading(
1739 &mut self,
1740 container: &mut &'a Node<'a, RefCell<Ast>>,
1741 line: &[u8],
1742 indented: bool,
1743 sc: &mut scanners::SetextChar,
1744 ) -> bool {
1745 if !self.detect_setext_heading(container, line, indented, sc) {
1746 return false;
1747 }
1748
1749 let has_content = {
1750 let mut ast = container.data.borrow_mut();
1751 self.resolve_reference_link_definitions(&mut ast.content)
1752 };
1753 if has_content {
1754 container.data.borrow_mut().value = NodeValue::Heading(NodeHeading {
1755 level: match sc {
1756 scanners::SetextChar::Equals => 1,
1757 scanners::SetextChar::Hyphen => 2,
1758 },
1759 setext: true,
1760 });
1761 let adv = line.len() - 1 - self.offset;
1762 self.advance_offset(line, adv, false);
1763 }
1764
1765 true
1766 }
1767
1768 fn detect_thematic_break(
1769 &mut self,
1770 container: &AstNode,
1771 line: &[u8],
1772 indented: bool,
1773 matched: &mut usize,
1774 all_matched: bool,
1775 ) -> bool {
1776 !indented
1777 && !matches!(
1778 (&container.data.borrow().value, all_matched),
1779 (&NodeValue::Paragraph, false)
1780 )
1781 && self.thematic_break_kill_pos <= self.first_nonspace
1782 && unwrap_into(self.scan_thematic_break(line), matched)
1783 }
1784
1785 fn handle_thematic_break(
1786 &mut self,
1787 container: &mut &'a Node<'a, RefCell<Ast>>,
1788 line: &[u8],
1789 indented: bool,
1790 matched: &mut usize,
1791 all_matched: bool,
1792 ) -> bool {
1793 if !self.detect_thematic_break(container, line, indented, matched, all_matched) {
1794 return false;
1795 }
1796
1797 *container = self.add_child(container, NodeValue::ThematicBreak, self.first_nonspace + 1);
1798
1799 let adv = line.len() - 1 - self.offset;
1800 self.advance_offset(line, adv, false);
1801
1802 true
1803 }
1804
1805 fn detect_footnote(
1806 &mut self,
1807 line: &[u8],
1808 indented: bool,
1809 matched: &mut usize,
1810 depth: usize,
1811 ) -> bool {
1812 !indented
1813 && self.options.extension.footnotes
1814 && depth < MAX_LIST_DEPTH
1815 && unwrap_into(
1816 scanners::footnote_definition(&line[self.first_nonspace..]),
1817 matched,
1818 )
1819 }
1820
1821 fn handle_footnote(
1822 &mut self,
1823 container: &mut &'a Node<'a, RefCell<Ast>>,
1824 line: &[u8],
1825 indented: bool,
1826 matched: &mut usize,
1827 depth: usize,
1828 ) -> bool {
1829 if !self.detect_footnote(line, indented, matched, depth) {
1830 return false;
1831 }
1832
1833 let mut c = &line[self.first_nonspace + 2..self.first_nonspace + *matched];
1834 c = c.split(|&e| e == b']').next().unwrap();
1835 let offset = self.first_nonspace + *matched - self.offset;
1836 self.advance_offset(line, offset, false);
1837 *container = self.add_child(
1838 container,
1839 NodeValue::FootnoteDefinition(NodeFootnoteDefinition {
1840 name: str::from_utf8(c).unwrap().to_string(),
1841 total_references: 0,
1842 }),
1843 self.first_nonspace + 1,
1844 );
1845 container.data.borrow_mut().internal_offset = *matched;
1846
1847 true
1848 }
1849
1850 fn detect_description_list(
1851 &mut self,
1852 container: &mut &'a Node<'a, RefCell<Ast>>,
1853 line: &[u8],
1854 indented: bool,
1855 matched: &mut usize,
1856 ) -> bool {
1857 !indented
1858 && self.options.extension.description_lists
1859 && unwrap_into(
1860 scanners::description_item_start(&line[self.first_nonspace..]),
1861 matched,
1862 )
1863 && self.parse_desc_list_details(container, *matched)
1864 }
1865
1866 fn handle_description_list(
1867 &mut self,
1868 container: &mut &'a Node<'a, RefCell<Ast>>,
1869 line: &[u8],
1870 indented: bool,
1871 matched: &mut usize,
1872 ) -> bool {
1873 if !self.detect_description_list(container, line, indented, matched) {
1874 return false;
1875 }
1876
1877 let offset = self.first_nonspace + *matched - self.offset;
1878 self.advance_offset(line, offset, false);
1879 if strings::is_space_or_tab(line[self.offset]) {
1880 self.advance_offset(line, 1, true);
1881 }
1882
1883 true
1884 }
1885
1886 fn detect_list(
1887 &mut self,
1888 container: &AstNode,
1889 line: &[u8],
1890 indented: bool,
1891 matched: &mut usize,
1892 depth: usize,
1893 nl: &mut NodeList,
1894 ) -> bool {
1895 (!indented || node_matches!(container, NodeValue::List(..)))
1896 && self.indent < 4
1897 && depth < MAX_LIST_DEPTH
1898 && unwrap_into_2(
1899 parse_list_marker(
1900 line,
1901 self.first_nonspace,
1902 node_matches!(container, NodeValue::Paragraph),
1903 ),
1904 matched,
1905 nl,
1906 )
1907 }
1908
1909 fn handle_list(
1910 &mut self,
1911 container: &mut &'a Node<'a, RefCell<Ast>>,
1912 line: &[u8],
1913 indented: bool,
1914 matched: &mut usize,
1915 depth: usize,
1916 nl: &mut NodeList,
1917 ) -> bool {
1918 if !self.detect_list(container, line, indented, matched, depth, nl) {
1919 return false;
1920 }
1921
1922 let offset = self.first_nonspace + *matched - self.offset;
1923 self.advance_offset(line, offset, false);
1924 let (save_partially_consumed_tab, save_offset, save_column) =
1925 (self.partially_consumed_tab, self.offset, self.column);
1926
1927 while self.column - save_column <= 5 && strings::is_space_or_tab(line[self.offset]) {
1928 self.advance_offset(line, 1, true);
1929 }
1930
1931 let i = self.column - save_column;
1932 if !(1..5).contains(&i) || strings::is_line_end_char(line[self.offset]) {
1933 nl.padding = *matched + 1;
1934 self.offset = save_offset;
1935 self.column = save_column;
1936 self.partially_consumed_tab = save_partially_consumed_tab;
1937 if i > 0 {
1938 self.advance_offset(line, 1, true);
1939 }
1940 } else {
1941 nl.padding = *matched + i;
1942 }
1943
1944 nl.marker_offset = self.indent;
1945
1946 if match container.data.borrow().value {
1947 NodeValue::List(ref mnl) => !lists_match(nl, mnl),
1948 _ => true,
1949 } {
1950 *container = self.add_child(container, NodeValue::List(*nl), self.first_nonspace + 1);
1951 }
1952
1953 *container = self.add_child(container, NodeValue::Item(*nl), self.first_nonspace + 1);
1954
1955 true
1956 }
1957
1958 fn detect_code_block(&mut self, indented: bool, maybe_lazy: bool) -> bool {
1959 indented && !maybe_lazy && !self.blank
1960 }
1961
1962 fn handle_code_block(
1963 &mut self,
1964 container: &mut &'a Node<'a, RefCell<Ast>>,
1965 line: &[u8],
1966 indented: bool,
1967 maybe_lazy: bool,
1968 ) -> bool {
1969 if !self.detect_code_block(indented, maybe_lazy) {
1970 return false;
1971 }
1972
1973 self.advance_offset(line, CODE_INDENT, true);
1974 let ncb = NodeCodeBlock {
1975 fenced: false,
1976 fence_char: 0,
1977 fence_length: 0,
1978 fence_offset: 0,
1979 info: String::new(),
1980 literal: String::new(),
1981 };
1982 *container = self.add_child(container, NodeValue::CodeBlock(ncb), self.offset + 1);
1983
1984 true
1985 }
1986
1987 fn open_new_blocks(&mut self, container: &mut &'a AstNode<'a>, line: &[u8], all_matched: bool) {
1988 let mut matched: usize = 0;
1989 let mut nl: NodeList = NodeList::default();
1990 let mut sc: scanners::SetextChar = scanners::SetextChar::Equals;
1991 let mut maybe_lazy = node_matches!(self.current, NodeValue::Paragraph);
1992 let mut depth = 0;
1993
1994 while !node_matches!(
1995 container,
1996 NodeValue::CodeBlock(..) | NodeValue::HtmlBlock(..)
1997 ) {
1998 depth += 1;
1999 self.find_first_nonspace(line);
2000 let indented = self.indent >= CODE_INDENT;
2001
2002 if self.handle_multiline_blockquote(container, line, indented, &mut matched)
2003 || self.handle_blockquote(container, line, indented)
2004 || self.handle_atx_heading(container, line, indented, &mut matched)
2005 || self.handle_code_fence(container, line, indented, &mut matched)
2006 || self.handle_html_block(container, line, indented, &mut matched)
2007 || self.handle_setext_heading(container, line, indented, &mut sc)
2008 || self.handle_thematic_break(container, line, indented, &mut matched, all_matched)
2009 || self.handle_footnote(container, line, indented, &mut matched, depth)
2010 || self.handle_description_list(container, line, indented, &mut matched)
2011 || self.handle_list(container, line, indented, &mut matched, depth, &mut nl)
2012 || self.handle_code_block(container, line, indented, maybe_lazy)
2013 {
2014 } else {
2016 let new_container = if !indented && self.options.extension.table {
2017 table::try_opening_block(self, container, line)
2018 } else {
2019 None
2020 };
2021
2022 match new_container {
2023 Some((new_container, replace, mark_visited)) => {
2024 if replace {
2025 container.insert_after(new_container);
2026 container.detach();
2027 *container = new_container;
2028 } else {
2029 *container = new_container;
2030 }
2031 if mark_visited {
2032 container.data.borrow_mut().table_visited = true;
2033 }
2034 }
2035 _ => break,
2036 }
2037 }
2038
2039 if container.data.borrow().value.accepts_lines() {
2040 break;
2041 }
2042
2043 maybe_lazy = false;
2044 }
2045 }
2046
2047 fn advance_offset(&mut self, line: &[u8], mut count: usize, columns: bool) {
2048 while count > 0 {
2049 match line[self.offset] {
2050 9 => {
2051 let chars_to_tab = TAB_STOP - (self.column % TAB_STOP);
2052 if columns {
2053 self.partially_consumed_tab = chars_to_tab > count;
2054 let chars_to_advance = min(count, chars_to_tab);
2055 self.column += chars_to_advance;
2056 self.offset += if self.partially_consumed_tab { 0 } else { 1 };
2057 count -= chars_to_advance;
2058 } else {
2059 self.partially_consumed_tab = false;
2060 self.column += chars_to_tab;
2061 self.offset += 1;
2062 count -= 1;
2063 }
2064 }
2065 _ => {
2066 self.partially_consumed_tab = false;
2067 self.offset += 1;
2068 self.column += 1;
2069 count -= 1;
2070 }
2071 }
2072 }
2073 }
2074
2075 fn parse_block_quote_prefix(&mut self, line: &[u8]) -> bool {
2076 let indent = self.indent;
2077 if indent <= 3 && line[self.first_nonspace] == b'>' && self.is_not_greentext(line) {
2078 self.advance_offset(line, indent + 1, true);
2079
2080 if strings::is_space_or_tab(line[self.offset]) {
2081 self.advance_offset(line, 1, true);
2082 }
2083
2084 return true;
2085 }
2086
2087 false
2088 }
2089
2090 fn parse_footnote_definition_block_prefix(&mut self, line: &[u8]) -> bool {
2091 if self.indent >= 4 {
2092 self.advance_offset(line, 4, true);
2093 true
2094 } else {
2095 line == b"\n" || line == b"\r\n"
2096 }
2097 }
2098
2099 fn parse_node_item_prefix(
2100 &mut self,
2101 line: &[u8],
2102 container: &'a AstNode<'a>,
2103 nl: &NodeList,
2104 ) -> bool {
2105 if self.indent >= nl.marker_offset + nl.padding {
2106 self.advance_offset(line, nl.marker_offset + nl.padding, true);
2107 true
2108 } else if self.blank && container.first_child().is_some() {
2109 let offset = self.first_nonspace - self.offset;
2110 self.advance_offset(line, offset, false);
2111 true
2112 } else {
2113 false
2114 }
2115 }
2116
2117 fn parse_description_item_prefix(
2118 &mut self,
2119 line: &[u8],
2120 container: &'a AstNode<'a>,
2121 di: &NodeDescriptionItem,
2122 ) -> bool {
2123 if self.indent >= di.marker_offset + di.padding {
2124 self.advance_offset(line, di.marker_offset + di.padding, true);
2125 true
2126 } else if self.blank && container.first_child().is_some() {
2127 let offset = self.first_nonspace - self.offset;
2128 self.advance_offset(line, offset, false);
2129 true
2130 } else {
2131 false
2132 }
2133 }
2134
2135 fn parse_code_block_prefix(
2136 &mut self,
2137 line: &[u8],
2138 container: &'a AstNode<'a>,
2139 ast: &mut Ast,
2140 should_continue: &mut bool,
2141 ) -> bool {
2142 let (fenced, fence_char, fence_length, fence_offset) = match ast.value {
2143 NodeValue::CodeBlock(ref ncb) => (
2144 ncb.fenced,
2145 ncb.fence_char,
2146 ncb.fence_length,
2147 ncb.fence_offset,
2148 ),
2149 _ => unreachable!(),
2150 };
2151
2152 if !fenced {
2153 if self.indent >= CODE_INDENT {
2154 self.advance_offset(line, CODE_INDENT, true);
2155 return true;
2156 } else if self.blank {
2157 let offset = self.first_nonspace - self.offset;
2158 self.advance_offset(line, offset, false);
2159 return true;
2160 }
2161 return false;
2162 }
2163
2164 let matched = if self.indent <= 3 && line[self.first_nonspace] == fence_char {
2165 scanners::close_code_fence(&line[self.first_nonspace..]).unwrap_or(0)
2166 } else {
2167 0
2168 };
2169
2170 if matched >= fence_length {
2171 *should_continue = false;
2172 self.advance_offset(line, matched, false);
2173 self.current = self.finalize_borrowed(container, ast).unwrap();
2174 return false;
2175 }
2176
2177 let mut i = fence_offset;
2178 while i > 0 && strings::is_space_or_tab(line[self.offset]) {
2179 self.advance_offset(line, 1, true);
2180 i -= 1;
2181 }
2182 true
2183 }
2184
2185 fn parse_html_block_prefix(&mut self, t: u8) -> bool {
2186 match t {
2187 1..=5 => true,
2188 6 | 7 => !self.blank,
2189 _ => unreachable!(),
2190 }
2191 }
2192
2193 fn parse_desc_list_details(&mut self, container: &mut &'a AstNode<'a>, matched: usize) -> bool {
2194 let mut tight = false;
2195 let last_child = match container.last_child() {
2196 Some(lc) => lc,
2197 None => {
2198 if !node_matches!(container, NodeValue::Paragraph) {
2201 return false;
2204 }
2205
2206 let parent = container.parent();
2207 if parent.is_none() {
2208 return false;
2209 }
2210
2211 tight = true;
2212 *container = parent.unwrap();
2213 container.last_child().unwrap()
2214 }
2215 };
2216
2217 if node_matches!(last_child, NodeValue::Paragraph) {
2218 last_child.detach();
2226 let last_child_sourcepos = last_child.data.borrow().sourcepos;
2227
2228 let list = match container.last_child() {
2244 Some(lc) if node_matches!(lc, NodeValue::DescriptionList) => {
2245 reopen_ast_nodes(lc);
2246 lc
2247 }
2248 _ => {
2249 let list = self.add_child(
2250 container,
2251 NodeValue::DescriptionList,
2252 self.first_nonspace + 1,
2253 );
2254 list.data.borrow_mut().sourcepos.start = last_child_sourcepos.start;
2255 list
2256 }
2257 };
2258
2259 let metadata = NodeDescriptionItem {
2260 marker_offset: self.indent,
2261 padding: matched,
2262 tight,
2263 };
2264
2265 let item = self.add_child(
2266 list,
2267 NodeValue::DescriptionItem(metadata),
2268 self.first_nonspace + 1,
2269 );
2270 item.data.borrow_mut().sourcepos.start = last_child_sourcepos.start;
2271 let term = self.add_child(item, NodeValue::DescriptionTerm, self.first_nonspace + 1);
2272 let details =
2273 self.add_child(item, NodeValue::DescriptionDetails, self.first_nonspace + 1);
2274
2275 term.append(last_child);
2276
2277 *container = details;
2278
2279 true
2280 } else if node_matches!(last_child, NodeValue::DescriptionItem(..)) {
2281 let parent = last_child.parent().unwrap();
2282 let tight = match last_child.data.borrow().value {
2283 NodeValue::DescriptionItem(ref ndi) => ndi.tight,
2284 _ => false,
2285 };
2286
2287 let metadata = NodeDescriptionItem {
2288 marker_offset: self.indent,
2289 padding: matched,
2290 tight,
2291 };
2292
2293 let item = self.add_child(
2294 parent,
2295 NodeValue::DescriptionItem(metadata),
2296 self.first_nonspace + 1,
2297 );
2298
2299 let details =
2300 self.add_child(item, NodeValue::DescriptionDetails, self.first_nonspace + 1);
2301
2302 *container = details;
2303
2304 true
2305 } else {
2306 false
2307 }
2308 }
2309
2310 fn parse_multiline_block_quote_prefix(
2311 &mut self,
2312 line: &[u8],
2313 container: &'a AstNode<'a>,
2314 ast: &mut Ast,
2315 should_continue: &mut bool,
2316 ) -> bool {
2317 let (fence_length, fence_offset) = match ast.value {
2318 NodeValue::MultilineBlockQuote(ref node_value) => {
2319 (node_value.fence_length, node_value.fence_offset)
2320 }
2321 _ => unreachable!(),
2322 };
2323
2324 let matched = if self.indent <= 3 && line[self.first_nonspace] == b'>' {
2325 scanners::close_multiline_block_quote_fence(&line[self.first_nonspace..]).unwrap_or(0)
2326 } else {
2327 0
2328 };
2329
2330 if matched >= fence_length {
2331 *should_continue = false;
2332 self.advance_offset(line, matched, false);
2333
2334 if nodes::last_child_is_open(container) {
2337 let child = container.last_child().unwrap();
2338 let child_ast = &mut *child.data.borrow_mut();
2339
2340 self.finalize_borrowed(child, child_ast).unwrap();
2341 }
2342
2343 self.current = self.finalize_borrowed(container, ast).unwrap();
2344 return false;
2345 }
2346
2347 let mut i = fence_offset;
2348 while i > 0 && strings::is_space_or_tab(line[self.offset]) {
2349 self.advance_offset(line, 1, true);
2350 i -= 1;
2351 }
2352 true
2353 }
2354
2355 fn add_child(
2356 &mut self,
2357 mut parent: &'a AstNode<'a>,
2358 value: NodeValue,
2359 start_column: usize,
2360 ) -> &'a AstNode<'a> {
2361 while !nodes::can_contain_type(parent, &value) {
2362 parent = self.finalize(parent).unwrap();
2363 }
2364
2365 assert!(start_column > 0);
2366
2367 let child = Ast::new(value, (self.line_number, start_column).into());
2368 let node = self.arena.alloc(Node::new(RefCell::new(child)));
2369 parent.append(node);
2370 node
2371 }
2372
2373 fn add_text_to_container(
2374 &mut self,
2375 mut container: &'a AstNode<'a>,
2376 last_matched_container: &'a AstNode<'a>,
2377 line: &[u8],
2378 ) {
2379 self.find_first_nonspace(line);
2380
2381 if self.blank {
2382 if let Some(last_child) = container.last_child() {
2383 last_child.data.borrow_mut().last_line_blank = true;
2384 }
2385 }
2386
2387 container.data.borrow_mut().last_line_blank = self.blank
2388 && match container.data.borrow().value {
2389 NodeValue::BlockQuote | NodeValue::Heading(..) | NodeValue::ThematicBreak => false,
2390 NodeValue::CodeBlock(ref ncb) => !ncb.fenced,
2391 NodeValue::Item(..) => {
2392 container.first_child().is_some()
2393 || container.data.borrow().sourcepos.start.line != self.line_number
2394 }
2395 NodeValue::MultilineBlockQuote(..) => false,
2396 _ => true,
2397 };
2398
2399 let mut tmp = container;
2400 while let Some(parent) = tmp.parent() {
2401 parent.data.borrow_mut().last_line_blank = false;
2402 tmp = parent;
2403 }
2404
2405 if !self.current.same_node(last_matched_container)
2406 && container.same_node(last_matched_container)
2407 && !self.blank
2408 && (!self.options.extension.greentext
2409 || !matches!(
2410 container.data.borrow().value,
2411 NodeValue::BlockQuote | NodeValue::Document
2412 ))
2413 && node_matches!(self.current, NodeValue::Paragraph)
2414 {
2415 self.add_line(self.current, line);
2416 } else {
2417 while !self.current.same_node(last_matched_container) {
2418 self.current = self.finalize(self.current).unwrap();
2419 }
2420
2421 let add_text_result = match container.data.borrow().value {
2422 NodeValue::CodeBlock(..) => AddTextResult::LiteralText,
2423 NodeValue::HtmlBlock(ref nhb) => AddTextResult::HtmlBlock(nhb.block_type),
2424 _ => AddTextResult::Otherwise,
2425 };
2426
2427 match add_text_result {
2428 AddTextResult::LiteralText => {
2429 self.add_line(container, line);
2430 }
2431 AddTextResult::HtmlBlock(block_type) => {
2432 self.add_line(container, line);
2433
2434 let matches_end_condition = match block_type {
2435 1 => scanners::html_block_end_1(&line[self.first_nonspace..]),
2436 2 => scanners::html_block_end_2(&line[self.first_nonspace..]),
2437 3 => scanners::html_block_end_3(&line[self.first_nonspace..]),
2438 4 => scanners::html_block_end_4(&line[self.first_nonspace..]),
2439 5 => scanners::html_block_end_5(&line[self.first_nonspace..]),
2440 _ => false,
2441 };
2442
2443 if matches_end_condition {
2444 container = self.finalize(container).unwrap();
2445 }
2446 }
2447 _ => {
2448 if self.blank {
2449 } else if container.data.borrow().value.accepts_lines() {
2451 let mut line: Vec<u8> = line.into();
2452 if let NodeValue::Heading(ref nh) = container.data.borrow().value {
2453 if !nh.setext {
2454 strings::chop_trailing_hashtags(&mut line);
2455 }
2456 };
2457 let count = self.first_nonspace - self.offset;
2458
2459 let have_line_text = self.first_nonspace <= line.len();
2472
2473 if have_line_text {
2474 self.advance_offset(&line, count, false);
2475 self.add_line(container, &line);
2476 }
2477 } else {
2478 container = self.add_child(
2479 container,
2480 NodeValue::Paragraph,
2481 self.first_nonspace + 1,
2482 );
2483 let count = self.first_nonspace - self.offset;
2484 self.advance_offset(line, count, false);
2485 self.add_line(container, line);
2486 }
2487 }
2488 }
2489
2490 self.current = container;
2491 }
2492 }
2493
2494 fn add_line(&mut self, node: &'a AstNode<'a>, line: &[u8]) {
2495 let mut ast = node.data.borrow_mut();
2496 assert!(ast.open);
2497 if self.partially_consumed_tab {
2498 self.offset += 1;
2499 let chars_to_tab = TAB_STOP - (self.column % TAB_STOP);
2500 for _ in 0..chars_to_tab {
2501 ast.content.push(' ');
2502 }
2503 }
2504 if self.offset < line.len() {
2505 ast.line_offsets.push(self.offset);
2509
2510 ast.content
2511 .push_str(str::from_utf8(&line[self.offset..]).unwrap());
2512 }
2513 }
2514
2515 fn finish(&mut self, remaining: Vec<u8>) -> &'a AstNode<'a> {
2516 if !remaining.is_empty() {
2517 self.process_line(&remaining);
2518 }
2519
2520 self.finalize_document();
2521 self.postprocess_text_nodes(self.root);
2522 self.root
2523 }
2524
2525 fn finalize_document(&mut self) {
2526 while !self.current.same_node(self.root) {
2527 self.current = self.finalize(self.current).unwrap();
2528 }
2529
2530 self.finalize(self.root);
2531
2532 self.refmap.max_ref_size = if self.total_size > 100000 {
2533 self.total_size
2534 } else {
2535 100000
2536 };
2537
2538 self.process_inlines();
2539 if self.options.extension.footnotes {
2540 self.process_footnotes();
2541 }
2542 }
2543
2544 fn finalize(&mut self, node: &'a AstNode<'a>) -> Option<&'a AstNode<'a>> {
2545 self.finalize_borrowed(node, &mut node.data.borrow_mut())
2546 }
2547
2548 fn resolve_reference_link_definitions(&mut self, content: &mut String) -> bool {
2549 let mut seeked = 0;
2550 {
2551 let mut pos = 0;
2552 let mut seek: &[u8] = content.as_bytes();
2553 while !seek.is_empty()
2554 && seek[0] == b'['
2555 && unwrap_into(self.parse_reference_inline(seek), &mut pos)
2556 {
2557 seek = &seek[pos..];
2558 seeked += pos;
2559 }
2560 }
2561
2562 if seeked != 0 {
2563 *content = content[seeked..].to_string();
2564 }
2565
2566 !strings::is_blank(content.as_bytes())
2567 }
2568
2569 fn finalize_borrowed(
2570 &mut self,
2571 node: &'a AstNode<'a>,
2572 ast: &mut Ast,
2573 ) -> Option<&'a AstNode<'a>> {
2574 assert!(ast.open);
2575 ast.open = false;
2576
2577 let content = &mut ast.content;
2578 let parent = node.parent();
2579
2580 if self.curline_len == 0 {
2581 ast.sourcepos.end = (self.line_number, self.last_line_length).into();
2582 } else if match ast.value {
2583 NodeValue::Document => true,
2584 NodeValue::CodeBlock(ref ncb) => ncb.fenced,
2585 NodeValue::MultilineBlockQuote(..) => true,
2586 _ => false,
2587 } {
2588 ast.sourcepos.end = (self.line_number, self.curline_end_col).into();
2589 } else {
2590 ast.sourcepos.end = (self.line_number - 1, self.last_line_length).into();
2591 }
2592
2593 match ast.value {
2594 NodeValue::Paragraph => {
2595 let has_content = self.resolve_reference_link_definitions(content);
2596 if !has_content {
2597 node.detach();
2598 }
2599 }
2600 NodeValue::CodeBlock(ref mut ncb) => {
2601 if !ncb.fenced {
2602 strings::remove_trailing_blank_lines(content);
2603 content.push('\n');
2604 } else {
2605 let mut pos = 0;
2606 while pos < content.len() {
2607 if strings::is_line_end_char(content.as_bytes()[pos]) {
2608 break;
2609 }
2610 pos += 1;
2611 }
2612 assert!(pos < content.len());
2613
2614 let mut tmp = entity::unescape_html(&content.as_bytes()[..pos]);
2615 strings::trim(&mut tmp);
2616 strings::unescape(&mut tmp);
2617 if tmp.is_empty() {
2618 ncb.info = self
2619 .options
2620 .parse
2621 .default_info_string
2622 .as_ref()
2623 .map_or(String::new(), |s| s.clone());
2624 } else {
2625 ncb.info = String::from_utf8(tmp).unwrap();
2626 }
2627
2628 if content.as_bytes()[pos] == b'\r' {
2629 pos += 1;
2630 }
2631 if content.as_bytes()[pos] == b'\n' {
2632 pos += 1;
2633 }
2634
2635 content.drain(..pos);
2636 }
2637 mem::swap(&mut ncb.literal, content);
2638 }
2639 NodeValue::HtmlBlock(ref mut nhb) => {
2640 mem::swap(&mut nhb.literal, content);
2641 }
2642 NodeValue::List(ref mut nl) => {
2643 nl.tight = true;
2644 let mut ch = node.first_child();
2645
2646 while let Some(item) = ch {
2647 if item.data.borrow().last_line_blank && item.next_sibling().is_some() {
2648 nl.tight = false;
2649 break;
2650 }
2651
2652 let mut subch = item.first_child();
2653 while let Some(subitem) = subch {
2654 if (item.next_sibling().is_some() || subitem.next_sibling().is_some())
2655 && nodes::ends_with_blank_line(subitem)
2656 {
2657 nl.tight = false;
2658 break;
2659 }
2660 subch = subitem.next_sibling();
2661 }
2662
2663 if !nl.tight {
2664 break;
2665 }
2666
2667 ch = item.next_sibling();
2668 }
2669 }
2670 _ => (),
2671 }
2672
2673 parent
2674 }
2675
2676 fn process_inlines(&mut self) {
2677 self.process_inlines_node(self.root);
2678 }
2679
2680 fn process_inlines_node(&mut self, node: &'a AstNode<'a>) {
2681 for node in node.descendants() {
2682 if node.data.borrow().value.contains_inlines() {
2683 self.parse_inlines(node);
2684 }
2685 }
2686 }
2687
2688 fn parse_inlines(&mut self, node: &'a AstNode<'a>) {
2689 let delimiter_arena = Arena::new();
2690 let node_data = node.data.borrow();
2691 let content = strings::rtrim_slice(node_data.content.as_bytes());
2692 let mut subj = inlines::Subject::new(
2693 self.arena,
2694 self.options,
2695 content,
2696 node_data.sourcepos.start.line,
2697 &mut self.refmap,
2698 &delimiter_arena,
2699 );
2700
2701 while subj.parse_inline(node) {}
2702
2703 subj.process_emphasis(0);
2704
2705 while subj.pop_bracket() {}
2706 }
2707
2708 fn process_footnotes(&mut self) {
2709 let mut map = HashMap::new();
2710 Self::find_footnote_definitions(self.root, &mut map);
2711
2712 let mut ix = 0;
2713 Self::find_footnote_references(self.root, &mut map, &mut ix);
2714
2715 if !map.is_empty() {
2716 Self::cleanup_footnote_definitions(self.root);
2720 }
2721
2722 if ix > 0 {
2723 let mut v = map.into_values().collect::<Vec<_>>();
2724 v.sort_unstable_by(|a, b| a.ix.cmp(&b.ix));
2725 for f in v {
2726 if f.ix.is_some() {
2727 match f.node.data.borrow_mut().value {
2728 NodeValue::FootnoteDefinition(ref mut nfd) => {
2729 nfd.name = f.name.to_string();
2730 nfd.total_references = f.total_references;
2731 }
2732 _ => unreachable!(),
2733 }
2734 self.root.append(f.node);
2735 }
2736 }
2737 }
2738 }
2739
2740 fn find_footnote_definitions(
2741 node: &'a AstNode<'a>,
2742 map: &mut HashMap<String, FootnoteDefinition<'a>>,
2743 ) {
2744 match node.data.borrow().value {
2745 NodeValue::FootnoteDefinition(ref nfd) => {
2746 map.insert(
2747 strings::normalize_label(&nfd.name, Case::Fold),
2748 FootnoteDefinition {
2749 ix: None,
2750 node,
2751 name: strings::normalize_label(&nfd.name, Case::Preserve),
2752 total_references: 0,
2753 },
2754 );
2755 }
2756 _ => {
2757 for n in node.children() {
2758 Self::find_footnote_definitions(n, map);
2759 }
2760 }
2761 }
2762 }
2763
2764 fn find_footnote_references(
2765 node: &'a AstNode<'a>,
2766 map: &mut HashMap<String, FootnoteDefinition>,
2767 ixp: &mut u32,
2768 ) {
2769 let mut ast = node.data.borrow_mut();
2770 let mut replace = None;
2771 match ast.value {
2772 NodeValue::FootnoteReference(ref mut nfr) => {
2773 let normalized = strings::normalize_label(&nfr.name, Case::Fold);
2774 if let Some(ref mut footnote) = map.get_mut(&normalized) {
2775 let ix = match footnote.ix {
2776 Some(ix) => ix,
2777 None => {
2778 *ixp += 1;
2779 footnote.ix = Some(*ixp);
2780 *ixp
2781 }
2782 };
2783 footnote.total_references += 1;
2784 nfr.ref_num = footnote.total_references;
2785 nfr.ix = ix;
2786 nfr.name = strings::normalize_label(&footnote.name, Case::Preserve);
2787 } else {
2788 replace = Some(nfr.name.clone());
2789 }
2790 }
2791 _ => {
2792 for n in node.children() {
2793 Self::find_footnote_references(n, map, ixp);
2794 }
2795 }
2796 }
2797
2798 if let Some(mut label) = replace {
2799 label.insert_str(0, "[^");
2800 label.push(']');
2801 ast.value = NodeValue::Text(label);
2802 }
2803 }
2804
2805 fn cleanup_footnote_definitions(node: &'a AstNode<'a>) {
2806 match node.data.borrow().value {
2807 NodeValue::FootnoteDefinition(_) => {
2808 node.detach();
2809 }
2810 _ => {
2811 for n in node.children() {
2812 Self::cleanup_footnote_definitions(n);
2813 }
2814 }
2815 }
2816 }
2817
2818 fn postprocess_text_nodes(&mut self, node: &'a AstNode<'a>) {
2819 let mut stack = vec![node];
2820 let mut children = vec![];
2821
2822 while let Some(node) = stack.pop() {
2823 let mut nch = node.first_child();
2824
2825 while let Some(n) = nch {
2826 let mut this_bracket = false;
2827 let n_ast = &mut n.data.borrow_mut();
2828 let mut sourcepos = n_ast.sourcepos;
2829
2830 loop {
2831 match n_ast.value {
2832 NodeValue::Text(ref mut root) => {
2834 let ns = match n.next_sibling() {
2835 Some(ns) => ns,
2836 _ => {
2837 self.postprocess_text_node(n, root, &mut sourcepos);
2839 break;
2840 }
2841 };
2842
2843 match ns.data.borrow().value {
2844 NodeValue::Text(ref adj) => {
2845 root.push_str(adj);
2846 sourcepos.end.column = ns.data.borrow().sourcepos.end.column;
2847 ns.detach();
2848 }
2849 _ => {
2850 self.postprocess_text_node(n, root, &mut sourcepos);
2852 break;
2853 }
2854 }
2855 }
2856 NodeValue::Link(..) | NodeValue::Image(..) | NodeValue::WikiLink(..) => {
2857 this_bracket = true;
2858 break;
2859 }
2860 _ => break,
2861 }
2862 }
2863
2864 n_ast.sourcepos = sourcepos;
2865
2866 if !this_bracket {
2867 children.push(n);
2868 }
2869
2870 nch = n.next_sibling();
2871 }
2872
2873 stack.extend(children.drain(..).rev());
2876 }
2877 }
2878
2879 fn postprocess_text_node(
2880 &mut self,
2881 node: &'a AstNode<'a>,
2882 text: &mut String,
2883 sourcepos: &mut Sourcepos,
2884 ) {
2885 if self.options.extension.tasklist {
2886 self.process_tasklist(node, text, sourcepos);
2887 }
2888
2889 if self.options.extension.autolink {
2890 autolink::process_autolinks(
2891 self.arena,
2892 node,
2893 text,
2894 self.options.parse.relaxed_autolinks,
2895 );
2896 }
2897 }
2898
2899 fn process_tasklist(
2900 &mut self,
2901 node: &'a AstNode<'a>,
2902 text: &mut String,
2903 sourcepos: &mut Sourcepos,
2904 ) {
2905 let (end, symbol) = match scanners::tasklist(text.as_bytes()) {
2906 Some(p) => p,
2907 None => return,
2908 };
2909
2910 let symbol = symbol as char;
2911
2912 if !self.options.parse.relaxed_tasklist_matching && !matches!(symbol, ' ' | 'x' | 'X') {
2913 return;
2914 }
2915
2916 let parent = node.parent().unwrap();
2917 if node.previous_sibling().is_some() || parent.previous_sibling().is_some() {
2918 return;
2919 }
2920
2921 if !node_matches!(parent, NodeValue::Paragraph) {
2922 return;
2923 }
2924
2925 let grandparent = parent.parent().unwrap();
2926 if !node_matches!(grandparent, NodeValue::Item(..)) {
2927 return;
2928 }
2929
2930 let great_grandparent = grandparent.parent().unwrap();
2931 if !node_matches!(great_grandparent, NodeValue::List(..)) {
2932 return;
2933 }
2934
2935 text.drain(..end);
2936
2937 sourcepos.start.column += end;
2941 parent.data.borrow_mut().sourcepos.start.column += end;
2942
2943 grandparent.data.borrow_mut().value =
2944 NodeValue::TaskItem(if symbol == ' ' { None } else { Some(symbol) });
2945
2946 if let NodeValue::List(ref mut list) = &mut great_grandparent.data.borrow_mut().value {
2947 list.is_task_list = true;
2948 }
2949 }
2950
2951 fn parse_reference_inline(&mut self, content: &[u8]) -> Option<usize> {
2952 let delimiter_arena = Arena::with_capacity(0);
2955 let mut subj = inlines::Subject::new(
2956 self.arena,
2957 self.options,
2958 content,
2959 0, &mut self.refmap,
2961 &delimiter_arena,
2962 );
2963
2964 let mut lab: String = match subj.link_label() {
2965 Some(lab) if !lab.is_empty() => lab.to_string(),
2966 _ => return None,
2967 };
2968
2969 if subj.peek_char() != Some(&(b':')) {
2970 return None;
2971 }
2972
2973 subj.pos += 1;
2974 subj.spnl();
2975 let (url, matchlen) = match inlines::manual_scan_link_url(&subj.input[subj.pos..]) {
2976 Some((url, matchlen)) => (url, matchlen),
2977 None => return None,
2978 };
2979 subj.pos += matchlen;
2980
2981 let beforetitle = subj.pos;
2982 subj.spnl();
2983 let title_search = if subj.pos == beforetitle {
2984 None
2985 } else {
2986 scanners::link_title(&subj.input[subj.pos..])
2987 };
2988 let title = match title_search {
2989 Some(matchlen) => {
2990 let t = &subj.input[subj.pos..subj.pos + matchlen];
2991 subj.pos += matchlen;
2992 t.to_vec()
2993 }
2994 _ => {
2995 subj.pos = beforetitle;
2996 vec![]
2997 }
2998 };
2999
3000 subj.skip_spaces();
3001 if !subj.skip_line_end() {
3002 if !title.is_empty() {
3003 subj.pos = beforetitle;
3004 subj.skip_spaces();
3005 if !subj.skip_line_end() {
3006 return None;
3007 }
3008 } else {
3009 return None;
3010 }
3011 }
3012
3013 lab = strings::normalize_label(&lab, Case::Fold);
3014 if !lab.is_empty() {
3015 subj.refmap.map.entry(lab).or_insert(ResolvedReference {
3016 url: String::from_utf8(strings::clean_url(url)).unwrap(),
3017 title: String::from_utf8(strings::clean_title(&title)).unwrap(),
3018 });
3019 }
3020 Some(subj.pos)
3021 }
3022}
3023
3024enum AddTextResult {
3025 LiteralText,
3026 HtmlBlock(u8),
3027 Otherwise,
3028}
3029
3030fn parse_list_marker(
3031 line: &[u8],
3032 mut pos: usize,
3033 interrupts_paragraph: bool,
3034) -> Option<(usize, NodeList)> {
3035 let mut c = line[pos];
3036 let startpos = pos;
3037
3038 if c == b'*' || c == b'-' || c == b'+' {
3039 pos += 1;
3040 if !isspace(line[pos]) {
3041 return None;
3042 }
3043
3044 if interrupts_paragraph {
3045 let mut i = pos;
3046 while strings::is_space_or_tab(line[i]) {
3047 i += 1;
3048 }
3049 if line[i] == b'\n' {
3050 return None;
3051 }
3052 }
3053
3054 return Some((
3055 pos - startpos,
3056 NodeList {
3057 list_type: ListType::Bullet,
3058 marker_offset: 0,
3059 padding: 0,
3060 start: 1,
3061 delimiter: ListDelimType::Period,
3062 bullet_char: c,
3063 tight: false,
3064 is_task_list: false,
3065 },
3066 ));
3067 } else if isdigit(c) {
3068 let mut start: usize = 0;
3069 let mut digits = 0;
3070
3071 loop {
3072 start = (10 * start) + (line[pos] - b'0') as usize;
3073 pos += 1;
3074 digits += 1;
3075
3076 if !(digits < 9 && isdigit(line[pos])) {
3077 break;
3078 }
3079 }
3080
3081 if interrupts_paragraph && start != 1 {
3082 return None;
3083 }
3084
3085 c = line[pos];
3086 if c != b'.' && c != b')' {
3087 return None;
3088 }
3089
3090 pos += 1;
3091
3092 if !isspace(line[pos]) {
3093 return None;
3094 }
3095
3096 if interrupts_paragraph {
3097 let mut i = pos;
3098 while strings::is_space_or_tab(line[i]) {
3099 i += 1;
3100 }
3101 if strings::is_line_end_char(line[i]) {
3102 return None;
3103 }
3104 }
3105
3106 return Some((
3107 pos - startpos,
3108 NodeList {
3109 list_type: ListType::Ordered,
3110 marker_offset: 0,
3111 padding: 0,
3112 start,
3113 delimiter: if c == b'.' {
3114 ListDelimType::Period
3115 } else {
3116 ListDelimType::Paren
3117 },
3118 bullet_char: 0,
3119 tight: false,
3120 is_task_list: false,
3121 },
3122 ));
3123 }
3124
3125 None
3126}
3127
3128pub fn unwrap_into<T>(t: Option<T>, out: &mut T) -> bool {
3129 match t {
3130 Some(v) => {
3131 *out = v;
3132 true
3133 }
3134 _ => false,
3135 }
3136}
3137
3138pub fn unwrap_into_copy<T: Copy>(t: Option<&T>, out: &mut T) -> bool {
3139 match t {
3140 Some(v) => {
3141 *out = *v;
3142 true
3143 }
3144 _ => false,
3145 }
3146}
3147
3148fn unwrap_into_2<T, U>(tu: Option<(T, U)>, out_t: &mut T, out_u: &mut U) -> bool {
3149 match tu {
3150 Some((t, u)) => {
3151 *out_t = t;
3152 *out_u = u;
3153 true
3154 }
3155 _ => false,
3156 }
3157}
3158
3159fn lists_match(list_data: &NodeList, item_data: &NodeList) -> bool {
3160 list_data.list_type == item_data.list_type
3161 && list_data.delimiter == item_data.delimiter
3162 && list_data.bullet_char == item_data.bullet_char
3163}
3164
3165fn reopen_ast_nodes<'a>(mut ast: &'a AstNode<'a>) {
3166 loop {
3167 ast.data.borrow_mut().open = true;
3168 ast = match ast.parent() {
3169 Some(p) => p,
3170 None => return,
3171 }
3172 }
3173}
3174
3175#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3176pub enum AutolinkType {
3177 Uri,
3178 Email,
3179}
3180
3181#[derive(Debug, Clone, Copy, Default)]
3182#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
3183pub enum ListStyleType {
3185 #[default]
3187 Dash = 45,
3188 Plus = 43,
3190 Star = 42,
3192}