parser: parse lists and list items
This commit is contained in:
		
					parent
					
						
							
								6befcad52a
							
						
					
				
			
			
				commit
				
					
						2f616c41b7
					
				
			
		
					 2 changed files with 186 additions and 8 deletions
				
			
		| 
						 | 
					@ -101,7 +101,7 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<'s, I, W> {
 | 
				
			||||||
                        Container::Blockquote => self.out.write_str("<blockquote")?,
 | 
					                        Container::Blockquote => self.out.write_str("<blockquote")?,
 | 
				
			||||||
                        Container::List(..) => todo!(),
 | 
					                        Container::List(..) => todo!(),
 | 
				
			||||||
                        Container::ListItem => self.out.write_str("<li")?,
 | 
					                        Container::ListItem => self.out.write_str("<li")?,
 | 
				
			||||||
                        Container::DescriptionList => self.out.write_str("<dl")?,
 | 
					                        Container::TaskListItem { .. } => todo!(),
 | 
				
			||||||
                        Container::DescriptionDetails => self.out.write_str("<dd")?,
 | 
					                        Container::DescriptionDetails => self.out.write_str("<dd")?,
 | 
				
			||||||
                        Container::Footnote { number, .. } => {
 | 
					                        Container::Footnote { number, .. } => {
 | 
				
			||||||
                            assert!(self.footnote_number.is_none());
 | 
					                            assert!(self.footnote_number.is_none());
 | 
				
			||||||
| 
						 | 
					@ -225,7 +225,7 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<'s, I, W> {
 | 
				
			||||||
                        Container::Blockquote => self.out.write_str("</blockquote>")?,
 | 
					                        Container::Blockquote => self.out.write_str("</blockquote>")?,
 | 
				
			||||||
                        Container::List(..) => todo!(),
 | 
					                        Container::List(..) => todo!(),
 | 
				
			||||||
                        Container::ListItem => self.out.write_str("</li>")?,
 | 
					                        Container::ListItem => self.out.write_str("</li>")?,
 | 
				
			||||||
                        Container::DescriptionList => self.out.write_str("</dl>")?,
 | 
					                        Container::TaskListItem { .. } => todo!(),
 | 
				
			||||||
                        Container::DescriptionDetails => self.out.write_str("</dd>")?,
 | 
					                        Container::DescriptionDetails => self.out.write_str("</dd>")?,
 | 
				
			||||||
                        Container::Footnote { number, .. } => {
 | 
					                        Container::Footnote { number, .. } => {
 | 
				
			||||||
                            if !self.footnote_backlink_written {
 | 
					                            if !self.footnote_backlink_written {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										190
									
								
								src/lib.rs
									
										
									
									
									
								
							
							
						
						
									
										190
									
								
								src/lib.rs
									
										
									
									
									
								
							| 
						 | 
					@ -36,8 +36,8 @@ pub enum Container<'s> {
 | 
				
			||||||
    List(List),
 | 
					    List(List),
 | 
				
			||||||
    /// An item of a list
 | 
					    /// An item of a list
 | 
				
			||||||
    ListItem,
 | 
					    ListItem,
 | 
				
			||||||
    /// A description list element.
 | 
					    /// An item of a task list, either checked or unchecked.
 | 
				
			||||||
    DescriptionList,
 | 
					    TaskListItem { checked: bool },
 | 
				
			||||||
    /// Details describing a term within a description list.
 | 
					    /// Details describing a term within a description list.
 | 
				
			||||||
    DescriptionDetails,
 | 
					    DescriptionDetails,
 | 
				
			||||||
    /// A footnote definition.
 | 
					    /// A footnote definition.
 | 
				
			||||||
| 
						 | 
					@ -99,7 +99,7 @@ impl<'s> Container<'s> {
 | 
				
			||||||
            Self::Blockquote
 | 
					            Self::Blockquote
 | 
				
			||||||
            | Self::List(..)
 | 
					            | Self::List(..)
 | 
				
			||||||
            | Self::ListItem
 | 
					            | Self::ListItem
 | 
				
			||||||
            | Self::DescriptionList
 | 
					            | Self::TaskListItem { .. }
 | 
				
			||||||
            | Self::DescriptionDetails
 | 
					            | Self::DescriptionDetails
 | 
				
			||||||
            | Self::Footnote { .. }
 | 
					            | Self::Footnote { .. }
 | 
				
			||||||
            | Self::Table
 | 
					            | Self::Table
 | 
				
			||||||
| 
						 | 
					@ -135,7 +135,7 @@ impl<'s> Container<'s> {
 | 
				
			||||||
            Self::Blockquote
 | 
					            Self::Blockquote
 | 
				
			||||||
            | Self::List(..)
 | 
					            | Self::List(..)
 | 
				
			||||||
            | Self::ListItem
 | 
					            | Self::ListItem
 | 
				
			||||||
            | Self::DescriptionList
 | 
					            | Self::TaskListItem { .. }
 | 
				
			||||||
            | Self::DescriptionDetails
 | 
					            | Self::DescriptionDetails
 | 
				
			||||||
            | Self::Footnote { .. }
 | 
					            | Self::Footnote { .. }
 | 
				
			||||||
            | Self::Table
 | 
					            | Self::Table
 | 
				
			||||||
| 
						 | 
					@ -260,6 +260,23 @@ impl<'s> Container<'s> {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl OrderedListNumbering {
 | 
				
			||||||
 | 
					    fn parse_number(self, n: &str) -> u32 {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Self::Decimal => n.parse().unwrap(),
 | 
				
			||||||
 | 
					            Self::AlphaLower | Self::AlphaUpper => 1,
 | 
				
			||||||
 | 
					            Self::RomanLower => 1,
 | 
				
			||||||
 | 
					            Self::RomanUpper => 1,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl OrderedListStyle {
 | 
				
			||||||
 | 
					    fn number(self, marker: &str) -> &str {
 | 
				
			||||||
 | 
					        &marker[usize::from(matches!(self, Self::ParenParen))..marker.len() - 1]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct Parser<'s> {
 | 
					pub struct Parser<'s> {
 | 
				
			||||||
    src: &'s str,
 | 
					    src: &'s str,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -482,8 +499,30 @@ impl<'s> Parser<'s> {
 | 
				
			||||||
                                self.footnotes.insert(content, self.tree.take_branch());
 | 
					                                self.footnotes.insert(content, self.tree.take_branch());
 | 
				
			||||||
                                continue;
 | 
					                                continue;
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            block::Container::List(ty) => todo!(),
 | 
					                            block::Container::List(ty) => match ty {
 | 
				
			||||||
                            block::Container::ListItem(ty) => todo!(),
 | 
					                                block::ListType::Unordered(..) => Container::List(List::Unordered),
 | 
				
			||||||
 | 
					                                block::ListType::Task => Container::List(List::Task),
 | 
				
			||||||
 | 
					                                block::ListType::Ordered(numbering, style) => {
 | 
				
			||||||
 | 
					                                    let marker = ev.span.of(self.src);
 | 
				
			||||||
 | 
					                                    let start = numbering.parse_number(style.number(marker)).max(1);
 | 
				
			||||||
 | 
					                                    Container::List(List::Ordered {
 | 
				
			||||||
 | 
					                                        numbering,
 | 
				
			||||||
 | 
					                                        style,
 | 
				
			||||||
 | 
					                                        start,
 | 
				
			||||||
 | 
					                                    })
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                block::ListType::Description => panic!(),
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                            block::Container::ListItem(ty) => {
 | 
				
			||||||
 | 
					                                if matches!(ty, block::ListType::Task) {
 | 
				
			||||||
 | 
					                                    let marker = ev.span.of(self.src);
 | 
				
			||||||
 | 
					                                    Container::TaskListItem {
 | 
				
			||||||
 | 
					                                        checked: marker.as_bytes()[3] != b' ',
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                } else {
 | 
				
			||||||
 | 
					                                    Container::ListItem
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
                        },
 | 
					                        },
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
                    if enter {
 | 
					                    if enter {
 | 
				
			||||||
| 
						 | 
					@ -545,6 +584,10 @@ mod test {
 | 
				
			||||||
    use super::Container::*;
 | 
					    use super::Container::*;
 | 
				
			||||||
    use super::Event::*;
 | 
					    use super::Event::*;
 | 
				
			||||||
    use super::LinkType;
 | 
					    use super::LinkType;
 | 
				
			||||||
 | 
					    use super::List;
 | 
				
			||||||
 | 
					    use super::List::*;
 | 
				
			||||||
 | 
					    use super::OrderedListNumbering::*;
 | 
				
			||||||
 | 
					    use super::OrderedListStyle::*;
 | 
				
			||||||
    use super::SpanLinkType;
 | 
					    use super::SpanLinkType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    macro_rules! test_parse {
 | 
					    macro_rules! test_parse {
 | 
				
			||||||
| 
						 | 
					@ -962,4 +1005,139 @@ mod test {
 | 
				
			||||||
            End(Paragraph),
 | 
					            End(Paragraph),
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn list_item_unordered() {
 | 
				
			||||||
 | 
					        test_parse!(
 | 
				
			||||||
 | 
					            "- abc",
 | 
				
			||||||
 | 
					            Start(List(List::Unordered), Attributes::new()),
 | 
				
			||||||
 | 
					            Start(ListItem, Attributes::new()),
 | 
				
			||||||
 | 
					            Start(Paragraph, Attributes::new()),
 | 
				
			||||||
 | 
					            Str("abc".into()),
 | 
				
			||||||
 | 
					            End(Paragraph),
 | 
				
			||||||
 | 
					            End(ListItem),
 | 
				
			||||||
 | 
					            End(List(List::Unordered)),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn list_item_ordered_decimal() {
 | 
				
			||||||
 | 
					        test_parse!(
 | 
				
			||||||
 | 
					            "123. abc",
 | 
				
			||||||
 | 
					            Start(
 | 
				
			||||||
 | 
					                List(List::Ordered {
 | 
				
			||||||
 | 
					                    numbering: Decimal,
 | 
				
			||||||
 | 
					                    style: Period,
 | 
				
			||||||
 | 
					                    start: 123
 | 
				
			||||||
 | 
					                }),
 | 
				
			||||||
 | 
					                Attributes::new()
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            Start(ListItem, Attributes::new()),
 | 
				
			||||||
 | 
					            Start(Paragraph, Attributes::new()),
 | 
				
			||||||
 | 
					            Str("abc".into()),
 | 
				
			||||||
 | 
					            End(Paragraph),
 | 
				
			||||||
 | 
					            End(ListItem),
 | 
				
			||||||
 | 
					            End(List(List::Ordered {
 | 
				
			||||||
 | 
					                numbering: Decimal,
 | 
				
			||||||
 | 
					                style: Period,
 | 
				
			||||||
 | 
					                start: 123
 | 
				
			||||||
 | 
					            })),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn list_mixed() {
 | 
				
			||||||
 | 
					        test_parse!(
 | 
				
			||||||
 | 
					            concat!(
 | 
				
			||||||
 | 
					                "- a\n",   //
 | 
				
			||||||
 | 
					                "+ b\n",   //
 | 
				
			||||||
 | 
					                "0) c\n",  //
 | 
				
			||||||
 | 
					                "0) d\n",  //
 | 
				
			||||||
 | 
					                "(b) e\n", //
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            Start(List(List::Unordered), Attributes::new()),
 | 
				
			||||||
 | 
					            Start(ListItem, Attributes::new()),
 | 
				
			||||||
 | 
					            Start(Paragraph, Attributes::new()),
 | 
				
			||||||
 | 
					            Str("a".into()),
 | 
				
			||||||
 | 
					            End(Paragraph),
 | 
				
			||||||
 | 
					            End(ListItem),
 | 
				
			||||||
 | 
					            End(List(List::Unordered)),
 | 
				
			||||||
 | 
					            Start(List(List::Unordered), Attributes::new()),
 | 
				
			||||||
 | 
					            Start(ListItem, Attributes::new()),
 | 
				
			||||||
 | 
					            Start(Paragraph, Attributes::new()),
 | 
				
			||||||
 | 
					            Str("b".into()),
 | 
				
			||||||
 | 
					            End(Paragraph),
 | 
				
			||||||
 | 
					            End(ListItem),
 | 
				
			||||||
 | 
					            End(List(List::Unordered)),
 | 
				
			||||||
 | 
					            Start(
 | 
				
			||||||
 | 
					                List(List::Ordered {
 | 
				
			||||||
 | 
					                    numbering: Decimal,
 | 
				
			||||||
 | 
					                    style: Paren,
 | 
				
			||||||
 | 
					                    start: 1,
 | 
				
			||||||
 | 
					                }),
 | 
				
			||||||
 | 
					                Attributes::new()
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            Start(ListItem, Attributes::new()),
 | 
				
			||||||
 | 
					            Start(Paragraph, Attributes::new()),
 | 
				
			||||||
 | 
					            Str("c".into()),
 | 
				
			||||||
 | 
					            End(Paragraph),
 | 
				
			||||||
 | 
					            End(ListItem),
 | 
				
			||||||
 | 
					            Start(ListItem, Attributes::new()),
 | 
				
			||||||
 | 
					            Start(Paragraph, Attributes::new()),
 | 
				
			||||||
 | 
					            Str("d".into()),
 | 
				
			||||||
 | 
					            End(Paragraph),
 | 
				
			||||||
 | 
					            End(ListItem),
 | 
				
			||||||
 | 
					            End(List(List::Ordered {
 | 
				
			||||||
 | 
					                numbering: Decimal,
 | 
				
			||||||
 | 
					                style: Paren,
 | 
				
			||||||
 | 
					                start: 1,
 | 
				
			||||||
 | 
					            })),
 | 
				
			||||||
 | 
					            Start(
 | 
				
			||||||
 | 
					                List(List::Ordered {
 | 
				
			||||||
 | 
					                    numbering: AlphaLower,
 | 
				
			||||||
 | 
					                    style: ParenParen,
 | 
				
			||||||
 | 
					                    start: 2
 | 
				
			||||||
 | 
					                }),
 | 
				
			||||||
 | 
					                Attributes::new()
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            Start(ListItem, Attributes::new()),
 | 
				
			||||||
 | 
					            Start(Paragraph, Attributes::new()),
 | 
				
			||||||
 | 
					            Str("e".into()),
 | 
				
			||||||
 | 
					            End(Paragraph),
 | 
				
			||||||
 | 
					            End(ListItem),
 | 
				
			||||||
 | 
					            End(List(List::Ordered {
 | 
				
			||||||
 | 
					                numbering: AlphaLower,
 | 
				
			||||||
 | 
					                style: ParenParen,
 | 
				
			||||||
 | 
					                start: 2,
 | 
				
			||||||
 | 
					            })),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn list_task() {
 | 
				
			||||||
 | 
					        test_parse!(
 | 
				
			||||||
 | 
					            concat!(
 | 
				
			||||||
 | 
					                "- [ ] a\n", //
 | 
				
			||||||
 | 
					                "- [x] b\n", //
 | 
				
			||||||
 | 
					                "- [X] c\n", //
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            Start(List(List::Task), Attributes::new()),
 | 
				
			||||||
 | 
					            Start(TaskListItem { checked: false }, Attributes::new()),
 | 
				
			||||||
 | 
					            Start(Paragraph, Attributes::new()),
 | 
				
			||||||
 | 
					            Str("a".into()),
 | 
				
			||||||
 | 
					            End(Paragraph),
 | 
				
			||||||
 | 
					            End(TaskListItem { checked: false }),
 | 
				
			||||||
 | 
					            Start(TaskListItem { checked: true }, Attributes::new()),
 | 
				
			||||||
 | 
					            Start(Paragraph, Attributes::new()),
 | 
				
			||||||
 | 
					            Str("b".into()),
 | 
				
			||||||
 | 
					            End(Paragraph),
 | 
				
			||||||
 | 
					            End(TaskListItem { checked: true }),
 | 
				
			||||||
 | 
					            Start(TaskListItem { checked: true }, Attributes::new()),
 | 
				
			||||||
 | 
					            Start(Paragraph, Attributes::new()),
 | 
				
			||||||
 | 
					            Str("c".into()),
 | 
				
			||||||
 | 
					            End(Paragraph),
 | 
				
			||||||
 | 
					            End(TaskListItem { checked: true }),
 | 
				
			||||||
 | 
					            End(List(List::Task)),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue