feat(parser): update list parsing

This commit is contained in:
PoiScript 2019-10-30 11:31:37 +08:00
parent ec334c2821
commit 73c6e9de8f
4 changed files with 408 additions and 307 deletions

View file

@ -1,67 +1,29 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::iter::once; use std::iter::once;
use memchr::memchr_iter; use memchr::{memchr, memchr_iter};
use nom::{
branch::alt,
bytes::complete::tag,
character::complete::{digit1, space0},
combinator::{map, recognize},
error::ParseError,
sequence::terminated,
IResult,
};
/// Plain List Element /// Plain List Element
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
#[derive(Debug)] #[derive(Debug)]
pub struct List { pub struct List {
/// List indent, number of whitespaces
pub indent: usize, pub indent: usize,
/// List's type, determined by the first item of this list
pub ordered: bool, pub ordered: bool,
} /// Numbers of blank lines between last list's line and next non-blank line
/// or buffer's end
impl List { pub post_blank: usize,
#[inline]
pub(crate) fn parse(text: &str) -> Option<(&str, List, &str)> {
let (indent, tail) = text
.find(|c| c != ' ')
.map(|off| (off, &text[off..]))
.unwrap_or((0, text));
let ordered = is_item(tail)?;
let mut last_end = 0;
let mut start = 0;
for i in memchr_iter(b'\n', text.as_bytes())
.map(|i| i + 1)
.chain(once(text.len()))
{
let line = &text[start..i];
if let Some(line_indent) = line.find(|c: char| !c.is_whitespace()) {
if line_indent < indent
|| (line_indent == indent && is_item(&line[line_indent..]).is_none())
{
return Some((
&text[start..],
List { indent, ordered },
&text[0..start - 1],
));
} else {
last_end = 0;
start = i;
continue;
}
} else {
// this line is empty
if last_end != 0 {
return Some((&text[i..], List { indent, ordered }, &text[0..last_end]));
} else {
last_end = start;
start = i;
continue;
}
}
}
if last_end != 0 {
Some(("", List { indent, ordered }, &text[0..last_end]))
} else {
Some(("", List { indent, ordered }, text))
}
}
} }
/// List Item Elemenet /// List Item Elemenet
@ -71,185 +33,287 @@ impl List {
pub struct ListItem<'a> { pub struct ListItem<'a> {
/// List item bullet /// List item bullet
pub bullet: Cow<'a, str>, pub bullet: Cow<'a, str>,
/// List item indent, number of whitespaces
pub indent: usize,
/// List item type
pub ordered: bool,
// TODO checkbox
// TODO counter
// TODO tag
} }
impl ListItem<'_> { impl ListItem<'_> {
#[inline] #[inline]
pub(crate) fn parse(text: &str, indent: usize) -> (&str, ListItem, &str) { pub(crate) fn parse(input: &str) -> Option<(&str, (ListItem, &str))> {
debug_assert!(&text[0..indent].trim().is_empty()); list_item::<()>(input).ok()
let off = &text[indent..].find(' ').unwrap() + 1 + indent;
let bytes = text.as_bytes();
let mut lines = memchr_iter(b'\n', bytes)
.map(|i| i + 1)
.chain(once(text.len()));
let mut pos = lines.next().unwrap();
for i in lines {
let line = &text[pos..i];
if let Some(line_indent) = line.find(|c: char| !c.is_whitespace()) {
if line_indent == indent {
return (
&text[pos..],
ListItem {
bullet: text[indent..off].into(),
},
&text[off..pos],
);
}
}
pos = i;
}
(
"",
ListItem {
bullet: text[indent..off].into(),
},
&text[off..],
)
} }
pub fn into_owned(self) -> ListItem<'static> { pub fn into_owned(self) -> ListItem<'static> {
ListItem { ListItem {
bullet: self.bullet.into_owned().into(), bullet: self.bullet.into_owned().into(),
indent: self.indent,
ordered: self.ordered,
} }
} }
} }
#[inline] fn list_item<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, (ListItem, &str), E> {
pub fn is_item(text: &str) -> Option<bool> { let (input, indent) = map(space0, |s: &str| s.len())(input)?;
let bytes = text.as_bytes(); let (input, bullet) = recognize(alt((
match bytes.get(0)? { tag("+ "),
b'*' | b'-' | b'+' => { tag("* "),
if text.len() > 1 && (bytes[1] == b' ' || bytes[1] == b'\n') { tag("- "),
Some(false) terminated(digit1, tag(". ")),
} else { )))(input)?;
None let (input, contents) = list_item_contents(input, indent);
Ok((
input,
(
ListItem {
bullet: bullet.into(),
indent,
ordered: bullet.starts_with(|c: char| c.is_ascii_digit()),
},
contents,
),
))
} }
}
b'0'..=b'9' => { fn list_item_contents(input: &str, indent: usize) -> (&str, &str) {
let i = bytes let mut last_end = memchr(b'\n', input.as_bytes())
.iter() .map(|i| i + 1)
.position(|&c| !c.is_ascii_digit()) .unwrap_or_else(|| input.len());
.unwrap_or_else(|| text.len() - 1);
if (bytes[i] == b'.' || bytes[i] == b')') for i in memchr_iter(b'\n', input.as_bytes())
&& text.len() > i + 1 .map(|i| i + 1)
&& (bytes[i + 1] == b' ' || bytes[i + 1] == b'\n') .chain(once(input.len()))
.skip(1)
{ {
Some(true) if input[last_end..i]
} else { .as_bytes()
None .iter()
} .all(u8::is_ascii_whitespace)
} {
_ => None, let x = memchr(b'\n', &input[i..].as_bytes())
.map(|ii| i + ii + 1)
.unwrap_or_else(|| input.len());
// two consecutive empty lines
if input[i..x].as_bytes().iter().all(u8::is_ascii_whitespace) {
return (&input[x..], &input[0..x]);
} }
} }
#[test] // line less or equally indented than the starting line
fn test_is_item() { if input[last_end..i]
assert_eq!(is_item("+ item"), Some(false)); .as_bytes()
assert_eq!(is_item("- item"), Some(false)); .iter()
assert_eq!(is_item("10. item"), Some(true)); .take(indent + 1)
assert_eq!(is_item("10) item"), Some(true)); .any(|c| !c.is_ascii_whitespace())
assert_eq!(is_item("1. item"), Some(true)); {
assert_eq!(is_item("1) item"), Some(true)); return (&input[last_end..], &input[0..last_end]);
assert_eq!(is_item("10. "), Some(true)); }
assert_eq!(is_item("10.\n"), Some(true));
assert_eq!(is_item("10."), None); last_end = i;
assert_eq!(is_item("+"), None); }
assert_eq!(is_item("-item"), None);
assert_eq!(is_item("+item"), None); ("", input)
} }
#[test] #[test]
fn list_parse() { fn parse() {
use nom::error::VerboseError;
assert_eq!( assert_eq!(
List::parse("+ item1\n+ item2"), list_item::<VerboseError<&str>>(
Some(( r#"+ item1
"", + item2"#
List { ),
Ok((
"+ item2",
(
ListItem {
bullet: "+ ".into(),
indent: 0, indent: 0,
ordered: false, ordered: false,
}, },
"+ item1\n+ item2" r#"item1
"#
)
)) ))
); );
assert_eq!( assert_eq!(
List::parse("* item1\n \n* item2"), list_item::<VerboseError<&str>>(
Some(( r#"* item1
"",
List { * item2"#
indent: 0, ),
ordered: false, Ok((
},
"* item1\n \n* item2"
))
);
assert_eq!(
List::parse("* item1\n \n \n* item2"),
Some((
"* item2", "* item2",
List { (
ListItem {
bullet: "* ".into(),
indent: 0, indent: 0,
ordered: false, ordered: false,
}, },
"* item1\n" r#"item1
"#
)
)) ))
); );
assert_eq!( assert_eq!(
List::parse("* item1\n \n "), list_item::<VerboseError<&str>>(
Some(( r#"* item1
"",
List {
* item2"#
),
Ok((
"* item2",
(
ListItem {
bullet: "* ".into(),
indent: 0, indent: 0,
ordered: false, ordered: false,
}, },
"* item1\n" r#"item1
"#
)
)) ))
); );
assert_eq!( assert_eq!(
List::parse("+ item1\n + item2\n "), list_item::<VerboseError<&str>>(
Some(( r#"* item1
"#
),
Ok((
"", "",
List { (
ListItem {
bullet: "* ".into(),
indent: 0, indent: 0,
ordered: false, ordered: false,
}, },
"+ item1\n + item2\n" r#"item1
"#
)
)) ))
); );
assert_eq!( assert_eq!(
List::parse("+ item1\n \n + item2\n \n+ item 3"), list_item::<VerboseError<&str>>(
Some(( r#"+ item1
+ item2
"#
),
Ok((
"", "",
List { (
ListItem {
bullet: "+ ".into(),
indent: 0, indent: 0,
ordered: false, ordered: false,
}, },
"+ item1\n \n + item2\n \n+ item 3" r#"item1
+ item2
"#
)
)) ))
); );
assert_eq!( assert_eq!(
List::parse(" + item1\n \n + item2"), list_item::<VerboseError<&str>>(
Some(( r#"+ item1
"",
List { + item2
+ item 3"#
),
Ok((
"+ item 3",
(
ListItem {
bullet: "+ ".into(),
indent: 0,
ordered: false,
},
r#"item1
+ item2
"#
)
))
);
assert_eq!(
list_item::<VerboseError<&str>>(
r#" + item1
+ item2"#
),
Ok((
" + item2",
(
ListItem {
bullet: "+ ".into(),
indent: 2, indent: 2,
ordered: false, ordered: false,
}, },
" + item1\n \n + item2" r#"item1
"#
)
)) ))
); );
assert_eq!( assert_eq!(
List::parse("+ 1\n\n - 2\n\n - 3\n\n+ 4"), list_item::<VerboseError<&str>>(
Some(( r#" 1. item1
"", 2. item2
List { 3. item3"#
),
Ok((
r#"2. item2
3. item3"#,
(
ListItem {
bullet: "1. ".into(),
indent: 2,
ordered: true,
},
r#"item1
"#
)
))
);
assert_eq!(
list_item::<VerboseError<&str>>(
r#"+ 1
- 2
- 3
+ 4"#
),
Ok((
"+ 4",
(
ListItem {
bullet: "+ ".into(),
indent: 0, indent: 0,
ordered: false, ordered: false,
}, },
"+ 1\n\n - 2\n\n - 3\n\n+ 4" r#"1
- 2
- 3
"#
)
)) ))
); );
} }

View file

@ -39,7 +39,12 @@ pub trait OrgHandler<E: From<Error>>: Default {
Headline { .. } => (), Headline { .. } => (),
List(_list) => (), List(_list) => (),
Italic => write!(w, "/")?, Italic => write!(w, "/")?,
ListItem(list_item) => write!(w, "{}", list_item.bullet)?, ListItem(list_item) => {
for _ in 0..list_item.indent {
write!(&mut w, " ")?;
}
write!(&mut w, "{}", list_item.bullet)?;
}
Paragraph { .. } => (), Paragraph { .. } => (),
Section => (), Section => (),
Strike => write!(w, "+")?, Strike => write!(w, "+")?,
@ -216,7 +221,9 @@ pub trait OrgHandler<E: From<Error>>: Default {
write_blank_lines(w, dyn_block.post_blank)?; write_blank_lines(w, dyn_block.post_blank)?;
} }
Headline { .. } => (), Headline { .. } => (),
List(_list) => (), List(list) => {
write_blank_lines(w, list.post_blank)?;
}
Italic => write!(w, "/")?, Italic => write!(w, "/")?,
ListItem(_) => (), ListItem(_) => (),
Paragraph { post_blank } => { Paragraph { post_blank } => {

View file

@ -20,34 +20,46 @@ use crate::elements::{
}; };
pub trait ElementArena<'a> { pub trait ElementArena<'a> {
fn append_element<T: Into<Element<'a>>>(&mut self, element: T, parent: NodeId) -> NodeId; fn append<T>(&mut self, element: T, parent: NodeId) -> NodeId
fn insert_before_last_child<T: Into<Element<'a>>>( where
&mut self, T: Into<Element<'a>>;
element: T, fn insert_before_last_child<T>(&mut self, element: T, parent: NodeId) -> NodeId
parent: NodeId, where
) -> NodeId; T: Into<Element<'a>>;
fn set<T>(&mut self, node: NodeId, element: T)
where
T: Into<Element<'a>>;
} }
impl<'a> ElementArena<'a> for Arena<Element<'a>> { impl<'a> ElementArena<'a> for Arena<Element<'a>> {
fn append_element<T: Into<Element<'a>>>(&mut self, element: T, parent: NodeId) -> NodeId { fn append<T>(&mut self, element: T, parent: NodeId) -> NodeId
where
T: Into<Element<'a>>,
{
let node = self.new_node(element.into()); let node = self.new_node(element.into());
parent.append(node, self); parent.append(node, self);
node node
} }
fn insert_before_last_child<T: Into<Element<'a>>>( fn insert_before_last_child<T>(&mut self, element: T, parent: NodeId) -> NodeId
&mut self, where
element: T, T: Into<Element<'a>>,
parent: NodeId, {
) -> NodeId {
if let Some(child) = self[parent].last_child() { if let Some(child) = self[parent].last_child() {
let node = self.new_node(element.into()); let node = self.new_node(element.into());
child.insert_before(node, self); child.insert_before(node, self);
node node
} else { } else {
self.append_element(element, parent) self.append(element, parent)
} }
} }
fn set<T>(&mut self, node: NodeId, element: T)
where
T: Into<Element<'a>>,
{
*self[node].get_mut() = element.into();
}
} }
pub struct OwnedArena<'a, 'b, 'c> { pub struct OwnedArena<'a, 'b, 'c> {
@ -65,50 +77,39 @@ impl<'a, 'b, 'c> OwnedArena<'a, 'b, 'c> {
} }
impl<'a> ElementArena<'a> for OwnedArena<'a, '_, '_> { impl<'a> ElementArena<'a> for OwnedArena<'a, '_, '_> {
fn append_element<T: Into<Element<'a>>>(&mut self, element: T, parent: NodeId) -> NodeId { fn append<T>(&mut self, element: T, parent: NodeId) -> NodeId
let node = self.arena.new_node(element.into().into_owned()); where
parent.append(node, self.arena); T: Into<Element<'a>>,
node {
self.arena.append(element.into().into_owned(), parent)
} }
fn insert_before_last_child<T: Into<Element<'a>>>( fn insert_before_last_child<T>(&mut self, element: T, parent: NodeId) -> NodeId
&mut self, where
element: T, T: Into<Element<'a>>,
parent: NodeId, {
) -> NodeId {
self.arena self.arena
.insert_before_last_child(element.into().into_owned(), parent) .insert_before_last_child(element.into().into_owned(), parent)
} }
fn set<T>(&mut self, node: NodeId, element: T)
where
T: Into<Element<'a>>,
{
self.arena.set(node, element.into().into_owned());
}
} }
#[derive(Debug)] #[derive(Debug)]
pub enum Container<'a> { pub enum Container<'a> {
// List
List {
content: &'a str,
node: NodeId,
indent: usize,
},
// Block, List Item // Block, List Item
Block { Block { content: &'a str, node: NodeId },
content: &'a str,
node: NodeId,
},
// Pargraph, Inline Markup // Pargraph, Inline Markup
Inline { Inline { content: &'a str, node: NodeId },
content: &'a str,
node: NodeId,
},
// Headline // Headline
Headline { Headline { content: &'a str, node: NodeId },
content: &'a str,
node: NodeId,
},
// Document // Document
Document { Document { content: &'a str, node: NodeId },
content: &'a str,
node: NodeId,
},
} }
pub fn parse_container<'a, T: ElementArena<'a>>( pub fn parse_container<'a, T: ElementArena<'a>>(
@ -132,13 +133,6 @@ pub fn parse_container<'a, T: ElementArena<'a>>(
Container::Inline { content, node } => { Container::Inline { content, node } => {
parse_inlines(arena, content, node, containers); parse_inlines(arena, content, node, containers);
} }
Container::List {
content,
node,
indent,
} => {
parse_list_items(arena, content, indent, node, containers);
}
} }
} }
} }
@ -151,7 +145,7 @@ pub fn parse_headline_content<'a, T: ElementArena<'a>>(
config: &ParseConfig, config: &ParseConfig,
) { ) {
let (tail, (title, content)) = Title::parse(content, config).unwrap(); let (tail, (title, content)) = Title::parse(content, config).unwrap();
let node = arena.append_element(title, parent); let node = arena.append(title, parent);
containers.push(Container::Inline { content, node }); containers.push(Container::Inline { content, node });
parse_section_and_headlines(arena, tail, parent, containers); parse_section_and_headlines(arena, tail, parent, containers);
} }
@ -171,12 +165,12 @@ pub fn parse_section_and_headlines<'a, T: ElementArena<'a>>(
for i in memchr_iter(b'\n', content.as_bytes()).chain(once(content.len())) { for i in memchr_iter(b'\n', content.as_bytes()).chain(once(content.len())) {
if let Some((mut tail, (headline_content, level))) = parse_headline(&content[last_end..]) { if let Some((mut tail, (headline_content, level))) = parse_headline(&content[last_end..]) {
if last_end != 0 { if last_end != 0 {
let node = arena.append_element(Element::Section, parent); let node = arena.append(Element::Section, parent);
let content = &content[0..last_end]; let content = &content[0..last_end];
containers.push(Container::Block { content, node }); containers.push(Container::Block { content, node });
} }
let node = arena.append_element(Element::Headline { level }, parent); let node = arena.append(Element::Headline { level }, parent);
containers.push(Container::Headline { containers.push(Container::Headline {
content: headline_content, content: headline_content,
node, node,
@ -184,7 +178,7 @@ pub fn parse_section_and_headlines<'a, T: ElementArena<'a>>(
while let Some((new_tail, (content, level))) = parse_headline(tail) { while let Some((new_tail, (content, level))) = parse_headline(tail) {
debug_assert_ne!(tail, new_tail); debug_assert_ne!(tail, new_tail);
let node = arena.append_element(Element::Headline { level }, parent); let node = arena.append(Element::Headline { level }, parent);
containers.push(Container::Headline { content, node }); containers.push(Container::Headline { content, node });
tail = new_tail; tail = new_tail;
} }
@ -193,7 +187,7 @@ pub fn parse_section_and_headlines<'a, T: ElementArena<'a>>(
last_end = i + 1; last_end = i + 1;
} }
let node = arena.append_element(Element::Section, parent); let node = arena.append(Element::Section, parent);
containers.push(Container::Block { content, node }); containers.push(Container::Block { content, node });
} }
@ -221,7 +215,7 @@ pub fn parse_blocks<'a, T: ElementArena<'a>>(
debug_assert_ne!(tail, tail_); debug_assert_ne!(tail, tail_);
tail = tail_; tail = tail_;
let node = arena.append_element( let node = arena.append(
Element::Paragraph { Element::Paragraph {
// including current line (&tail[0..i]) // including current line (&tail[0..i])
post_blank: blank + 1, post_blank: blank + 1,
@ -259,7 +253,7 @@ pub fn parse_blocks<'a, T: ElementArena<'a>>(
} }
if !text.is_empty() { if !text.is_empty() {
let node = arena.append_element(Element::Paragraph { post_blank: 0 }, parent); let node = arena.append(Element::Paragraph { post_blank: 0 }, parent);
containers.push(Container::Inline { containers.push(Container::Inline {
content: &text[0..pos].trim_end(), content: &text[0..pos].trim_end(),
@ -276,19 +270,12 @@ pub fn parse_block<'a, T: ElementArena<'a>>(
) -> Option<&'a str> { ) -> Option<&'a str> {
// footnote definitions must be start at column 0 // footnote definitions must be start at column 0
if let Some((tail, (fn_def, content))) = FnDef::parse(contents) { if let Some((tail, (fn_def, content))) = FnDef::parse(contents) {
let node = arena.append_element(fn_def, parent); let node = arena.append(fn_def, parent);
containers.push(Container::Block { content, node }); containers.push(Container::Block { content, node });
return Some(tail); return Some(tail);
} }
if let Some((tail, list, content)) = List::parse(contents) { if let Some(tail) = parse_list(arena, contents, parent, containers) {
let indent = list.indent;
let node = arena.append_element(list, parent);
containers.push(Container::List {
content,
node,
indent,
});
return Some(tail); return Some(tail);
} }
@ -297,7 +284,7 @@ pub fn parse_block<'a, T: ElementArena<'a>>(
match contents.as_bytes().get(0)? { match contents.as_bytes().get(0)? {
b'C' => { b'C' => {
let (tail, clock) = Clock::parse(contents)?; let (tail, clock) = Clock::parse(contents)?;
arena.append_element(clock, parent); arena.append(clock, parent);
Some(tail) Some(tail)
} }
b'\'' => { b'\'' => {
@ -306,17 +293,17 @@ pub fn parse_block<'a, T: ElementArena<'a>>(
} }
b'-' => { b'-' => {
let (tail, rule) = Rule::parse(contents)?; let (tail, rule) = Rule::parse(contents)?;
arena.append_element(rule, parent); arena.append(rule, parent);
Some(tail) Some(tail)
} }
b':' => { b':' => {
if let Some((tail, (drawer, content))) = Drawer::parse(contents) { if let Some((tail, (drawer, content))) = Drawer::parse(contents) {
let node = arena.append_element(drawer, parent); let node = arena.append(drawer, parent);
containers.push(Container::Block { content, node }); containers.push(Container::Block { content, node });
Some(tail) Some(tail)
} else { } else {
let (tail, fixed_width) = FixedWidth::parse(contents)?; let (tail, fixed_width) = FixedWidth::parse(contents)?;
arena.append_element(fixed_width, parent); arena.append(fixed_width, parent);
Some(tail) Some(tail)
} }
} }
@ -337,12 +324,12 @@ pub fn parse_block<'a, T: ElementArena<'a>>(
); );
Some(tail) Some(tail)
} else if let Some((tail, (dyn_block, content))) = DynBlock::parse(contents) { } else if let Some((tail, (dyn_block, content))) = DynBlock::parse(contents) {
let node = arena.append_element(dyn_block, parent); let node = arena.append(dyn_block, parent);
containers.push(Container::Block { content, node }); containers.push(Container::Block { content, node });
Some(tail) Some(tail)
} else if let Some((tail, (key, optional, value, blank))) = parse_keyword(contents) { } else if let Some((tail, (key, optional, value, blank))) = parse_keyword(contents) {
if (&*key).eq_ignore_ascii_case("CALL") { if (&*key).eq_ignore_ascii_case("CALL") {
arena.append_element( arena.append(
BabelCall { BabelCall {
value: value.into(), value: value.into(),
post_blank: blank, post_blank: blank,
@ -350,7 +337,7 @@ pub fn parse_block<'a, T: ElementArena<'a>>(
parent, parent,
); );
} else { } else {
arena.append_element( arena.append(
Keyword { Keyword {
key: key.into(), key: key.into(),
optional: optional.map(Into::into), optional: optional.map(Into::into),
@ -363,7 +350,7 @@ pub fn parse_block<'a, T: ElementArena<'a>>(
Some(tail) Some(tail)
} else { } else {
let (tail, comment) = Comment::parse(contents)?; let (tail, comment) = Comment::parse(contents)?;
arena.append_element(comment, parent); arena.append(comment, parent);
Some(tail) Some(tail)
} }
} }
@ -383,7 +370,7 @@ pub fn match_block<'a, T: ElementArena<'a>>(
match &*name.to_uppercase() { match &*name.to_uppercase() {
"CENTER" => { "CENTER" => {
let (content, pre_blank) = blank_lines(content); let (content, pre_blank) = blank_lines(content);
let node = arena.append_element( let node = arena.append(
CenterBlock { CenterBlock {
parameters, parameters,
pre_blank, pre_blank,
@ -395,7 +382,7 @@ pub fn match_block<'a, T: ElementArena<'a>>(
} }
"QUOTE" => { "QUOTE" => {
let (content, pre_blank) = blank_lines(content); let (content, pre_blank) = blank_lines(content);
let node = arena.append_element( let node = arena.append(
QuoteBlock { QuoteBlock {
parameters, parameters,
pre_blank, pre_blank,
@ -407,7 +394,7 @@ pub fn match_block<'a, T: ElementArena<'a>>(
} }
"VERSE" => { "VERSE" => {
let (content, pre_blank) = blank_lines(content); let (content, pre_blank) = blank_lines(content);
let node = arena.append_element( let node = arena.append(
VerseBlock { VerseBlock {
parameters, parameters,
pre_blank, pre_blank,
@ -418,7 +405,7 @@ pub fn match_block<'a, T: ElementArena<'a>>(
containers.push(Container::Block { content, node }); containers.push(Container::Block { content, node });
} }
"COMMENT" => { "COMMENT" => {
arena.append_element( arena.append(
CommentBlock { CommentBlock {
data: parameters, data: parameters,
contents: content.into(), contents: content.into(),
@ -428,7 +415,7 @@ pub fn match_block<'a, T: ElementArena<'a>>(
); );
} }
"EXAMPLE" => { "EXAMPLE" => {
arena.append_element( arena.append(
ExampleBlock { ExampleBlock {
data: parameters, data: parameters,
contents: content.into(), contents: content.into(),
@ -438,7 +425,7 @@ pub fn match_block<'a, T: ElementArena<'a>>(
); );
} }
"EXPORT" => { "EXPORT" => {
arena.append_element( arena.append(
ExportBlock { ExportBlock {
data: parameters.unwrap_or_default(), data: parameters.unwrap_or_default(),
contents: content.into(), contents: content.into(),
@ -457,7 +444,7 @@ pub fn match_block<'a, T: ElementArena<'a>>(
None => (Cow::Borrowed(""), Cow::Borrowed("")), None => (Cow::Borrowed(""), Cow::Borrowed("")),
_ => unreachable!("`parse_block_element` returns `Some(Cow::Borrowed)` or `None`"), _ => unreachable!("`parse_block_element` returns `Some(Cow::Borrowed)` or `None`"),
}; };
arena.append_element( arena.append(
SourceBlock { SourceBlock {
arguments, arguments,
language, language,
@ -469,7 +456,7 @@ pub fn match_block<'a, T: ElementArena<'a>>(
} }
_ => { _ => {
let (content, pre_blank) = blank_lines(content); let (content, pre_blank) = blank_lines(content);
let node = arena.append_element( let node = arena.append(
SpecialBlock { SpecialBlock {
parameters, parameters,
name, name,
@ -553,7 +540,7 @@ pub fn parse_inlines<'a, T: ElementArena<'a>>(
} }
if !tail.is_empty() { if !tail.is_empty() {
arena.append_element(Element::Text { value: tail.into() }, parent); arena.append(Element::Text { value: tail.into() }, parent);
} }
} }
@ -570,111 +557,136 @@ pub fn parse_inline<'a, T: ElementArena<'a>>(
match contents.as_bytes()[0] { match contents.as_bytes()[0] {
b'@' => { b'@' => {
let (tail, snippet) = Snippet::parse(contents)?; let (tail, snippet) = Snippet::parse(contents)?;
arena.append_element(snippet, parent); arena.append(snippet, parent);
Some(tail) Some(tail)
} }
b'{' => { b'{' => {
let (tail, macros) = Macros::parse(contents)?; let (tail, macros) = Macros::parse(contents)?;
arena.append_element(macros, parent); arena.append(macros, parent);
Some(tail) Some(tail)
} }
b'<' => { b'<' => {
if let Some((tail, _content)) = parse_radio_target(contents) { if let Some((tail, _content)) = parse_radio_target(contents) {
arena.append_element(Element::RadioTarget, parent); arena.append(Element::RadioTarget, parent);
Some(tail) Some(tail)
} else if let Some((tail, target)) = Target::parse(contents) { } else if let Some((tail, target)) = Target::parse(contents) {
arena.append_element(target, parent); arena.append(target, parent);
Some(tail) Some(tail)
} else if let Some((tail, timestamp)) = Timestamp::parse_active(contents) { } else if let Some((tail, timestamp)) = Timestamp::parse_active(contents) {
arena.append_element(timestamp, parent); arena.append(timestamp, parent);
Some(tail) Some(tail)
} else { } else {
let (tail, timestamp) = Timestamp::parse_diary(contents)?; let (tail, timestamp) = Timestamp::parse_diary(contents)?;
arena.append_element(timestamp, parent); arena.append(timestamp, parent);
Some(tail) Some(tail)
} }
} }
b'[' => { b'[' => {
if let Some((tail, fn_ref)) = FnRef::parse(contents) { if let Some((tail, fn_ref)) = FnRef::parse(contents) {
arena.append_element(fn_ref, parent); arena.append(fn_ref, parent);
Some(tail) Some(tail)
} else if let Some((tail, link)) = Link::parse(contents) { } else if let Some((tail, link)) = Link::parse(contents) {
arena.append_element(link, parent); arena.append(link, parent);
Some(tail) Some(tail)
} else if let Some((tail, cookie)) = Cookie::parse(contents) { } else if let Some((tail, cookie)) = Cookie::parse(contents) {
arena.append_element(cookie, parent); arena.append(cookie, parent);
Some(tail) Some(tail)
} else { } else {
let (tail, timestamp) = Timestamp::parse_inactive(contents)?; let (tail, timestamp) = Timestamp::parse_inactive(contents)?;
arena.append_element(timestamp, parent); arena.append(timestamp, parent);
Some(tail) Some(tail)
} }
} }
b'*' => { b'*' => {
let (tail, content) = parse_emphasis(contents, b'*')?; let (tail, content) = parse_emphasis(contents, b'*')?;
let node = arena.append_element(Element::Bold, parent); let node = arena.append(Element::Bold, parent);
containers.push(Container::Inline { content, node }); containers.push(Container::Inline { content, node });
Some(tail) Some(tail)
} }
b'+' => { b'+' => {
let (tail, content) = parse_emphasis(contents, b'+')?; let (tail, content) = parse_emphasis(contents, b'+')?;
let node = arena.append_element(Element::Strike, parent); let node = arena.append(Element::Strike, parent);
containers.push(Container::Inline { content, node }); containers.push(Container::Inline { content, node });
Some(tail) Some(tail)
} }
b'/' => { b'/' => {
let (tail, content) = parse_emphasis(contents, b'/')?; let (tail, content) = parse_emphasis(contents, b'/')?;
let node = arena.append_element(Element::Italic, parent); let node = arena.append(Element::Italic, parent);
containers.push(Container::Inline { content, node }); containers.push(Container::Inline { content, node });
Some(tail) Some(tail)
} }
b'_' => { b'_' => {
let (tail, content) = parse_emphasis(contents, b'_')?; let (tail, content) = parse_emphasis(contents, b'_')?;
let node = arena.append_element(Element::Underline, parent); let node = arena.append(Element::Underline, parent);
containers.push(Container::Inline { content, node }); containers.push(Container::Inline { content, node });
Some(tail) Some(tail)
} }
b'=' => { b'=' => {
let (tail, value) = parse_emphasis(contents, b'=')?; let (tail, value) = parse_emphasis(contents, b'=')?;
let value = value.into(); let value = value.into();
arena.append_element(Element::Verbatim { value }, parent); arena.append(Element::Verbatim { value }, parent);
Some(tail) Some(tail)
} }
b'~' => { b'~' => {
let (tail, value) = parse_emphasis(contents, b'~')?; let (tail, value) = parse_emphasis(contents, b'~')?;
let value = value.into(); let value = value.into();
arena.append_element(Element::Code { value }, parent); arena.append(Element::Code { value }, parent);
Some(tail) Some(tail)
} }
b's' => { b's' => {
let (tail, inline_src) = InlineSrc::parse(contents)?; let (tail, inline_src) = InlineSrc::parse(contents)?;
arena.append_element(inline_src, parent); arena.append(inline_src, parent);
Some(tail) Some(tail)
} }
b'c' => { b'c' => {
let (tail, inline_call) = InlineCall::parse(contents)?; let (tail, inline_call) = InlineCall::parse(contents)?;
arena.append_element(inline_call, parent); arena.append(inline_call, parent);
Some(tail) Some(tail)
} }
_ => None, _ => None,
} }
} }
pub fn parse_list_items<'a, T: ElementArena<'a>>( pub fn parse_list<'a, T: ElementArena<'a>>(
arena: &mut T, arena: &mut T,
mut contents: &'a str, contents: &'a str,
indent: usize,
parent: NodeId, parent: NodeId,
containers: &mut Vec<Container<'a>>, containers: &mut Vec<Container<'a>>,
) { ) -> Option<&'a str> {
while !contents.is_empty() { let (mut tail, (first_item, content)) = ListItem::parse(contents)?;
let (tail, list_item, content) = ListItem::parse(contents, indent); let first_item_indent = first_item.indent;
let node = arena.append_element(list_item, parent); let first_item_ordered = first_item.ordered;
let parent = arena.append(Element::Document { pre_blank: 0 }, parent); // placeholder
let node = arena.append(first_item, parent);
containers.push(Container::Block { content, node }); containers.push(Container::Block { content, node });
contents = tail;
while let Some((tail_, (item, content))) = ListItem::parse(tail) {
if item.indent == first_item_indent {
let node = arena.append(item, parent);
containers.push(Container::Block { content, node });
debug_assert_ne!(tail, tail_);
tail = tail_;
} else {
break;
} }
} }
let (tail, blank) = blank_lines(tail);
arena.set(
parent,
List {
indent: first_item_indent,
ordered: first_item_ordered,
post_blank: blank,
},
);
Some(tail)
}
pub fn parse_table<'a, T: ElementArena<'a>>( pub fn parse_table<'a, T: ElementArena<'a>>(
arena: &mut T, arena: &mut T,
contents: &'a str, contents: &'a str,
@ -682,16 +694,16 @@ pub fn parse_table<'a, T: ElementArena<'a>>(
parent: NodeId, parent: NodeId,
) -> Option<&'a str> { ) -> Option<&'a str> {
if contents.trim_start().starts_with('|') { if contents.trim_start().starts_with('|') {
let table_node = arena.append_element(Table::Org { tblfm: None }, parent); let table_node = arena.append(Table::Org { tblfm: None }, parent);
let mut last_end = 0; let mut last_end = 0;
for start in memchr_iter(b'\n', contents.as_bytes()).chain(once(contents.len())) { for start in memchr_iter(b'\n', contents.as_bytes()).chain(once(contents.len())) {
let line = contents[last_end..start].trim(); let line = contents[last_end..start].trim();
match TableRow::parse(line) { match TableRow::parse(line) {
Some(TableRow::Standard) => { Some(TableRow::Standard) => {
let row_node = arena.append_element(TableRow::Standard, table_node); let row_node = arena.append(TableRow::Standard, table_node);
for cell in line[1..].split_terminator('|') { for cell in line[1..].split_terminator('|') {
let cell_node = arena.append_element(Element::TableCell, row_node); let cell_node = arena.append(Element::TableCell, row_node);
containers.push(Container::Inline { containers.push(Container::Inline {
content: cell.trim(), content: cell.trim(),
node: cell_node, node: cell_node,
@ -699,7 +711,7 @@ pub fn parse_table<'a, T: ElementArena<'a>>(
} }
} }
Some(TableRow::Rule) => { Some(TableRow::Rule) => {
arena.append_element(TableRow::Rule, table_node); arena.append(TableRow::Rule, table_node);
} }
None => return Some(&contents[last_end..]), None => return Some(&contents[last_end..]),
} }
@ -710,7 +722,7 @@ pub fn parse_table<'a, T: ElementArena<'a>>(
} else { } else {
let (tail, value) = parse_table_el(contents)?; let (tail, value) = parse_table_el(contents)?;
let value = value.into(); let value = value.into();
arena.append_element(Table::TableEl { value }, parent); arena.append(Table::TableEl { value }, parent);
Some(tail) Some(tail)
} }

View file

@ -51,6 +51,24 @@ COMMENT
#+BEGIN_EXAMPLE #+BEGIN_EXAMPLE
#+END_EXAMPLE #+END_EXAMPLE
1. 1
2. 2
3. 3
+ 1
+ 2
- 3
- 4
+ 5
"#; "#;
#[test] #[test]