block: emit list events around list items
This commit is contained in:
		
					parent
					
						
							
								3123bd1a57
							
						
					
				
			
			
				commit
				
					
						50632204a3
					
				
			
		
					 3 changed files with 125 additions and 4 deletions
				
			
		
							
								
								
									
										116
									
								
								src/block.rs
									
										
									
									
									
								
							
							
						
						
									
										116
									
								
								src/block.rs
									
										
									
									
									
								
							| 
						 | 
					@ -80,6 +80,9 @@ pub enum Container {
 | 
				
			||||||
    /// Span is class specifier, possibly empty.
 | 
					    /// Span is class specifier, possibly empty.
 | 
				
			||||||
    Div,
 | 
					    Div,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Span is the list marker of the first list item in the list.
 | 
				
			||||||
 | 
					    List(ListType),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Span is the list marker.
 | 
					    /// Span is the list marker.
 | 
				
			||||||
    ListItem(ListType),
 | 
					    ListItem(ListType),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -95,10 +98,22 @@ pub enum ListType {
 | 
				
			||||||
    Description,
 | 
					    Description,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					struct OpenList {
 | 
				
			||||||
 | 
					    /// Type of the list, used to determine whether this list should be continued or a new one
 | 
				
			||||||
 | 
					    /// should be created.
 | 
				
			||||||
 | 
					    ty: ListType,
 | 
				
			||||||
 | 
					    /// Depth in the tree where the direct list items of the list are. Needed to determine when to
 | 
				
			||||||
 | 
					    /// close the list.
 | 
				
			||||||
 | 
					    depth: u16,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Parser for block-level tree structure of entire document.
 | 
					/// Parser for block-level tree structure of entire document.
 | 
				
			||||||
struct TreeParser<'s> {
 | 
					struct TreeParser<'s> {
 | 
				
			||||||
    src: &'s str,
 | 
					    src: &'s str,
 | 
				
			||||||
    tree: TreeBuilder,
 | 
					    tree: TreeBuilder,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    lists_open: Vec<OpenList>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'s> TreeParser<'s> {
 | 
					impl<'s> TreeParser<'s> {
 | 
				
			||||||
| 
						 | 
					@ -107,6 +122,7 @@ impl<'s> TreeParser<'s> {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            src,
 | 
					            src,
 | 
				
			||||||
            tree: TreeBuilder::new(),
 | 
					            tree: TreeBuilder::new(),
 | 
				
			||||||
 | 
					            lists_open: Vec::new(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -121,6 +137,9 @@ impl<'s> TreeParser<'s> {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            line_pos += line_count;
 | 
					            line_pos += line_count;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        for _ in self.lists_open.drain(..) {
 | 
				
			||||||
 | 
					            self.tree.exit(); // list
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        self.tree.finish()
 | 
					        self.tree.finish()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -190,6 +209,7 @@ impl<'s> TreeParser<'s> {
 | 
				
			||||||
                    Block::Container(c) => {
 | 
					                    Block::Container(c) => {
 | 
				
			||||||
                        let (skip_chars, skip_lines_suffix) = match c {
 | 
					                        let (skip_chars, skip_lines_suffix) = match c {
 | 
				
			||||||
                            Blockquote => (2, 0),
 | 
					                            Blockquote => (2, 0),
 | 
				
			||||||
 | 
					                            List(..) => panic!(),
 | 
				
			||||||
                            ListItem(..) | Footnote => (indent, 0),
 | 
					                            ListItem(..) | Footnote => (indent, 0),
 | 
				
			||||||
                            Div => (0, 1),
 | 
					                            Div => (0, 1),
 | 
				
			||||||
                        };
 | 
					                        };
 | 
				
			||||||
| 
						 | 
					@ -211,11 +231,36 @@ impl<'s> TreeParser<'s> {
 | 
				
			||||||
                                *sp = sp.skip(skip);
 | 
					                                *sp = sp.skip(skip);
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if let Container::ListItem(ty) = c {
 | 
				
			||||||
 | 
					                            if self
 | 
				
			||||||
 | 
					                                .lists_open
 | 
				
			||||||
 | 
					                                .last()
 | 
				
			||||||
 | 
					                                .map_or(true, |OpenList { depth, .. }| {
 | 
				
			||||||
 | 
					                                    usize::from(*depth) < self.tree.depth()
 | 
				
			||||||
 | 
					                                })
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                self.tree.enter(Node::Container(Container::List(ty)), span);
 | 
				
			||||||
 | 
					                                self.lists_open.push(OpenList {
 | 
				
			||||||
 | 
					                                    ty,
 | 
				
			||||||
 | 
					                                    depth: self.tree.depth().try_into().unwrap(),
 | 
				
			||||||
 | 
					                                });
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        self.tree.enter(Node::Container(c), span);
 | 
					                        self.tree.enter(Node::Container(c), span);
 | 
				
			||||||
                        let mut l = 0;
 | 
					                        let mut l = 0;
 | 
				
			||||||
                        while l < line_count_inner {
 | 
					                        while l < line_count_inner {
 | 
				
			||||||
                            l += self.parse_block(&mut lines[l..line_count_inner]);
 | 
					                            l += self.parse_block(&mut lines[l..line_count_inner]);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if let Some(OpenList { depth, .. }) = self.lists_open.last() {
 | 
				
			||||||
 | 
					                            assert!(usize::from(*depth) <= self.tree.depth());
 | 
				
			||||||
 | 
					                            if self.tree.depth() == (*depth).into() {
 | 
				
			||||||
 | 
					                                self.tree.exit(); // list
 | 
				
			||||||
 | 
					                                self.lists_open.pop();
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        self.tree.exit();
 | 
					                        self.tree.exit();
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
| 
						 | 
					@ -390,6 +435,7 @@ impl BlockParser {
 | 
				
			||||||
                !((&mut c).take(fence_length).all(|c| c == fence)
 | 
					                !((&mut c).take(fence_length).all(|c| c == fence)
 | 
				
			||||||
                    && c.next().map_or(true, char::is_whitespace))
 | 
					                    && c.next().map_or(true, char::is_whitespace))
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            Block::Container(List(..)) => panic!(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -759,14 +805,80 @@ mod test {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn parse_list() {
 | 
					    fn parse_list_single_item() {
 | 
				
			||||||
        test_parse!(
 | 
					        test_parse!(
 | 
				
			||||||
            "- abc\n",
 | 
					            concat!(
 | 
				
			||||||
 | 
					                "- abc\n",
 | 
				
			||||||
 | 
					                "\n",
 | 
				
			||||||
 | 
					                "\n", //
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            (Enter(Container(List(Unordered(b'-')))), "-"),
 | 
				
			||||||
            (Enter(Container(ListItem(Unordered(b'-')))), "-"),
 | 
					            (Enter(Container(ListItem(Unordered(b'-')))), "-"),
 | 
				
			||||||
            (Enter(Leaf(Paragraph)), ""),
 | 
					            (Enter(Leaf(Paragraph)), ""),
 | 
				
			||||||
            (Inline, "abc"),
 | 
					            (Inline, "abc"),
 | 
				
			||||||
            (Exit(Leaf(Paragraph)), ""),
 | 
					            (Exit(Leaf(Paragraph)), ""),
 | 
				
			||||||
 | 
					            (Atom(Blankline), "\n"),
 | 
				
			||||||
 | 
					            (Atom(Blankline), "\n"),
 | 
				
			||||||
            (Exit(Container(ListItem(Unordered(b'-')))), "-"),
 | 
					            (Exit(Container(ListItem(Unordered(b'-')))), "-"),
 | 
				
			||||||
 | 
					            (Exit(Container(List(Unordered(b'-')))), "-"),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn parse_list_multi_item() {
 | 
				
			||||||
 | 
					        test_parse!(
 | 
				
			||||||
 | 
					            "- abc\n\n\n- def\n\n",
 | 
				
			||||||
 | 
					            (Enter(Container(List(Unordered(b'-')))), "-"),
 | 
				
			||||||
 | 
					            (Enter(Container(ListItem(Unordered(b'-')))), "-"),
 | 
				
			||||||
 | 
					            (Enter(Leaf(Paragraph)), ""),
 | 
				
			||||||
 | 
					            (Inline, "abc"),
 | 
				
			||||||
 | 
					            (Exit(Leaf(Paragraph)), ""),
 | 
				
			||||||
 | 
					            (Atom(Blankline), "\n"),
 | 
				
			||||||
 | 
					            (Atom(Blankline), "\n"),
 | 
				
			||||||
 | 
					            (Exit(Container(ListItem(Unordered(b'-')))), "-"),
 | 
				
			||||||
 | 
					            (Enter(Container(ListItem(Unordered(b'-')))), "-"),
 | 
				
			||||||
 | 
					            (Enter(Leaf(Paragraph)), ""),
 | 
				
			||||||
 | 
					            (Inline, "def"),
 | 
				
			||||||
 | 
					            (Exit(Leaf(Paragraph)), ""),
 | 
				
			||||||
 | 
					            (Atom(Blankline), "\n"),
 | 
				
			||||||
 | 
					            (Exit(Container(ListItem(Unordered(b'-')))), "-"),
 | 
				
			||||||
 | 
					            (Exit(Container(List(Unordered(b'-')))), "-"),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn parse_list_nest() {
 | 
				
			||||||
 | 
					        test_parse!(
 | 
				
			||||||
 | 
					            concat!(
 | 
				
			||||||
 | 
					                "- a\n",     //
 | 
				
			||||||
 | 
					                "\n",        //
 | 
				
			||||||
 | 
					                "   - aa\n", //
 | 
				
			||||||
 | 
					                "\n",        //
 | 
				
			||||||
 | 
					                "\n",        //
 | 
				
			||||||
 | 
					                "- b\n",     //
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            (Enter(Container(List(Unordered(b'-')))), "-"),
 | 
				
			||||||
 | 
					            (Enter(Container(ListItem(Unordered(b'-')))), "-"),
 | 
				
			||||||
 | 
					            (Enter(Leaf(Paragraph)), ""),
 | 
				
			||||||
 | 
					            (Inline, "a"),
 | 
				
			||||||
 | 
					            (Exit(Leaf(Paragraph)), ""),
 | 
				
			||||||
 | 
					            (Atom(Blankline), "\n"),
 | 
				
			||||||
 | 
					            (Enter(Container(List(Unordered(b'-')))), "-"),
 | 
				
			||||||
 | 
					            (Enter(Container(ListItem(Unordered(b'-')))), "-"),
 | 
				
			||||||
 | 
					            (Enter(Leaf(Paragraph)), ""),
 | 
				
			||||||
 | 
					            (Inline, "aa"),
 | 
				
			||||||
 | 
					            (Exit(Leaf(Paragraph)), ""),
 | 
				
			||||||
 | 
					            (Atom(Blankline), "\n"),
 | 
				
			||||||
 | 
					            (Atom(Blankline), "\n"),
 | 
				
			||||||
 | 
					            (Exit(Container(ListItem(Unordered(b'-')))), "-"),
 | 
				
			||||||
 | 
					            (Exit(Container(List(Unordered(b'-')))), "-"),
 | 
				
			||||||
 | 
					            (Exit(Container(ListItem(Unordered(b'-')))), "-"),
 | 
				
			||||||
 | 
					            (Enter(Container(ListItem(Unordered(b'-')))), "-"),
 | 
				
			||||||
 | 
					            (Enter(Leaf(Paragraph)), ""),
 | 
				
			||||||
 | 
					            (Inline, "b"),
 | 
				
			||||||
 | 
					            (Exit(Leaf(Paragraph)), ""),
 | 
				
			||||||
 | 
					            (Exit(Container(ListItem(Unordered(b'-')))), "-"),
 | 
				
			||||||
 | 
					            (Exit(Container(List(Unordered(b'-')))), "-"),
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -480,7 +480,7 @@ impl<'s> Parser<'s> {
 | 
				
			||||||
                                self.footnotes.insert(content, self.tree.take_branch());
 | 
					                                self.footnotes.insert(content, self.tree.take_branch());
 | 
				
			||||||
                                continue;
 | 
					                                continue;
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            block::Container::ListItem(..) => panic!(),
 | 
					                            _ => panic!(),
 | 
				
			||||||
                        };
 | 
					                        };
 | 
				
			||||||
                        Event::Start(container, attributes)
 | 
					                        Event::Start(container, attributes)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
| 
						 | 
					@ -494,7 +494,7 @@ impl<'s> Parser<'s> {
 | 
				
			||||||
                                class: (!ev.span.is_empty()).then(|| content),
 | 
					                                class: (!ev.span.is_empty()).then(|| content),
 | 
				
			||||||
                            },
 | 
					                            },
 | 
				
			||||||
                            block::Container::Footnote => panic!(),
 | 
					                            block::Container::Footnote => panic!(),
 | 
				
			||||||
                            block::Container::ListItem(..) => panic!(),
 | 
					                            _ => panic!(),
 | 
				
			||||||
                        };
 | 
					                        };
 | 
				
			||||||
                        Event::End(container)
 | 
					                        Event::End(container)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -146,6 +146,7 @@ pub struct Builder<C, A> {
 | 
				
			||||||
    nodes: Vec<Node<C, A>>,
 | 
					    nodes: Vec<Node<C, A>>,
 | 
				
			||||||
    branch: Vec<NodeIndex>,
 | 
					    branch: Vec<NodeIndex>,
 | 
				
			||||||
    head: Option<NodeIndex>,
 | 
					    head: Option<NodeIndex>,
 | 
				
			||||||
 | 
					    depth: usize,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<C: Clone, A: Clone> Builder<C, A> {
 | 
					impl<C: Clone, A: Clone> Builder<C, A> {
 | 
				
			||||||
| 
						 | 
					@ -158,6 +159,7 @@ impl<C: Clone, A: Clone> Builder<C, A> {
 | 
				
			||||||
            }],
 | 
					            }],
 | 
				
			||||||
            branch: vec![],
 | 
					            branch: vec![],
 | 
				
			||||||
            head: Some(NodeIndex::root()),
 | 
					            head: Some(NodeIndex::root()),
 | 
				
			||||||
 | 
					            depth: 0,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -178,6 +180,7 @@ impl<C: Clone, A: Clone> Builder<C, A> {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub(super) fn enter(&mut self, c: C, span: Span) {
 | 
					    pub(super) fn enter(&mut self, c: C, span: Span) {
 | 
				
			||||||
 | 
					        self.depth += 1;
 | 
				
			||||||
        self.add_node(Node {
 | 
					        self.add_node(Node {
 | 
				
			||||||
            span,
 | 
					            span,
 | 
				
			||||||
            kind: NodeKind::Container(c, None),
 | 
					            kind: NodeKind::Container(c, None),
 | 
				
			||||||
| 
						 | 
					@ -186,6 +189,7 @@ impl<C: Clone, A: Clone> Builder<C, A> {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub(super) fn exit(&mut self) {
 | 
					    pub(super) fn exit(&mut self) {
 | 
				
			||||||
 | 
					        self.depth -= 1;
 | 
				
			||||||
        if self.head.is_some() {
 | 
					        if self.head.is_some() {
 | 
				
			||||||
            self.head = None;
 | 
					            self.head = None;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
| 
						 | 
					@ -195,6 +199,7 @@ impl<C: Clone, A: Clone> Builder<C, A> {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub(super) fn finish(self) -> Tree<C, A> {
 | 
					    pub(super) fn finish(self) -> Tree<C, A> {
 | 
				
			||||||
 | 
					        assert_eq!(self.depth, 0);
 | 
				
			||||||
        let head = self.nodes[NodeIndex::root().index()].next;
 | 
					        let head = self.nodes[NodeIndex::root().index()].next;
 | 
				
			||||||
        Tree {
 | 
					        Tree {
 | 
				
			||||||
            nodes: self.nodes.into_boxed_slice().into(),
 | 
					            nodes: self.nodes.into_boxed_slice().into(),
 | 
				
			||||||
| 
						 | 
					@ -203,6 +208,10 @@ impl<C: Clone, A: Clone> Builder<C, A> {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub(super) fn depth(&self) -> usize {
 | 
				
			||||||
 | 
					        self.depth
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn add_node(&mut self, node: Node<C, A>) {
 | 
					    fn add_node(&mut self, node: Node<C, A>) {
 | 
				
			||||||
        let ni = NodeIndex::new(self.nodes.len());
 | 
					        let ni = NodeIndex::new(self.nodes.len());
 | 
				
			||||||
        self.nodes.push(node);
 | 
					        self.nodes.push(node);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue