feat(parser): update list parsing
This commit is contained in:
parent
ec334c2821
commit
73c6e9de8f
|
@ -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,
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list_item_contents(input: &str, indent: usize) -> (&str, &str) {
|
||||||
|
let mut last_end = memchr(b'\n', input.as_bytes())
|
||||||
|
.map(|i| i + 1)
|
||||||
|
.unwrap_or_else(|| input.len());
|
||||||
|
|
||||||
|
for i in memchr_iter(b'\n', input.as_bytes())
|
||||||
|
.map(|i| i + 1)
|
||||||
|
.chain(once(input.len()))
|
||||||
|
.skip(1)
|
||||||
|
{
|
||||||
|
if input[last_end..i]
|
||||||
|
.as_bytes()
|
||||||
|
.iter()
|
||||||
|
.all(u8::is_ascii_whitespace)
|
||||||
|
{
|
||||||
|
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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b'0'..=b'9' => {
|
|
||||||
let i = bytes
|
// line less or equally indented than the starting line
|
||||||
.iter()
|
if input[last_end..i]
|
||||||
.position(|&c| !c.is_ascii_digit())
|
.as_bytes()
|
||||||
.unwrap_or_else(|| text.len() - 1);
|
.iter()
|
||||||
if (bytes[i] == b'.' || bytes[i] == b')')
|
.take(indent + 1)
|
||||||
&& text.len() > i + 1
|
.any(|c| !c.is_ascii_whitespace())
|
||||||
&& (bytes[i + 1] == b' ' || bytes[i + 1] == b'\n')
|
{
|
||||||
{
|
return (&input[last_end..], &input[0..last_end]);
|
||||||
Some(true)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => None,
|
|
||||||
|
last_end = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
("", input)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_item() {
|
fn parse() {
|
||||||
assert_eq!(is_item("+ item"), Some(false));
|
use nom::error::VerboseError;
|
||||||
assert_eq!(is_item("- item"), Some(false));
|
|
||||||
assert_eq!(is_item("10. item"), Some(true));
|
|
||||||
assert_eq!(is_item("10) item"), Some(true));
|
|
||||||
assert_eq!(is_item("1. item"), Some(true));
|
|
||||||
assert_eq!(is_item("1) item"), Some(true));
|
|
||||||
assert_eq!(is_item("10. "), Some(true));
|
|
||||||
assert_eq!(is_item("10.\n"), Some(true));
|
|
||||||
assert_eq!(is_item("10."), None);
|
|
||||||
assert_eq!(is_item("+"), None);
|
|
||||||
assert_eq!(is_item("-item"), None);
|
|
||||||
assert_eq!(is_item("+item"), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn list_parse() {
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
List::parse("+ item1\n+ item2"),
|
list_item::<VerboseError<&str>>(
|
||||||
Some((
|
r#"+ item1
|
||||||
"",
|
+ item2"#
|
||||||
List {
|
),
|
||||||
indent: 0,
|
Ok((
|
||||||
ordered: false,
|
"+ item2",
|
||||||
},
|
(
|
||||||
"+ item1\n+ item2"
|
ListItem {
|
||||||
|
bullet: "+ ".into(),
|
||||||
|
indent: 0,
|
||||||
|
ordered: false,
|
||||||
|
},
|
||||||
|
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 {
|
(
|
||||||
indent: 0,
|
ListItem {
|
||||||
ordered: false,
|
bullet: "* ".into(),
|
||||||
},
|
indent: 0,
|
||||||
"* item1\n"
|
ordered: false,
|
||||||
|
},
|
||||||
|
r#"item1
|
||||||
|
|
||||||
|
"#
|
||||||
|
)
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
List::parse("* item1\n \n "),
|
list_item::<VerboseError<&str>>(
|
||||||
Some((
|
r#"* item1
|
||||||
"",
|
|
||||||
List {
|
|
||||||
indent: 0,
|
* item2"#
|
||||||
ordered: false,
|
),
|
||||||
},
|
Ok((
|
||||||
"* item1\n"
|
"* item2",
|
||||||
|
(
|
||||||
|
ListItem {
|
||||||
|
bullet: "* ".into(),
|
||||||
|
indent: 0,
|
||||||
|
ordered: false,
|
||||||
|
},
|
||||||
|
r#"item1
|
||||||
|
|
||||||
|
|
||||||
|
"#
|
||||||
|
)
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
List::parse("+ item1\n + item2\n "),
|
list_item::<VerboseError<&str>>(
|
||||||
Some((
|
r#"* item1
|
||||||
|
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
Ok((
|
||||||
"",
|
"",
|
||||||
List {
|
(
|
||||||
indent: 0,
|
ListItem {
|
||||||
ordered: false,
|
bullet: "* ".into(),
|
||||||
},
|
indent: 0,
|
||||||
"+ item1\n + item2\n"
|
ordered: false,
|
||||||
|
},
|
||||||
|
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 {
|
(
|
||||||
indent: 0,
|
ListItem {
|
||||||
ordered: false,
|
bullet: "+ ".into(),
|
||||||
},
|
indent: 0,
|
||||||
"+ item1\n \n + item2\n \n+ item 3"
|
ordered: false,
|
||||||
|
},
|
||||||
|
r#"item1
|
||||||
|
+ item2
|
||||||
|
"#
|
||||||
|
)
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
List::parse(" + item1\n \n + item2"),
|
list_item::<VerboseError<&str>>(
|
||||||
Some((
|
r#"+ item1
|
||||||
"",
|
|
||||||
List {
|
+ item2
|
||||||
indent: 2,
|
|
||||||
ordered: false,
|
+ item 3"#
|
||||||
},
|
),
|
||||||
" + item1\n \n + item2"
|
Ok((
|
||||||
|
"+ item 3",
|
||||||
|
(
|
||||||
|
ListItem {
|
||||||
|
bullet: "+ ".into(),
|
||||||
|
indent: 0,
|
||||||
|
ordered: false,
|
||||||
|
},
|
||||||
|
r#"item1
|
||||||
|
|
||||||
|
+ item2
|
||||||
|
|
||||||
|
"#
|
||||||
|
)
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
List::parse("+ 1\n\n - 2\n\n - 3\n\n+ 4"),
|
list_item::<VerboseError<&str>>(
|
||||||
Some((
|
r#" + item1
|
||||||
"",
|
|
||||||
List {
|
+ item2"#
|
||||||
indent: 0,
|
),
|
||||||
ordered: false,
|
Ok((
|
||||||
},
|
" + item2",
|
||||||
"+ 1\n\n - 2\n\n - 3\n\n+ 4"
|
(
|
||||||
|
ListItem {
|
||||||
|
bullet: "+ ".into(),
|
||||||
|
indent: 2,
|
||||||
|
ordered: false,
|
||||||
|
},
|
||||||
|
r#"item1
|
||||||
|
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
list_item::<VerboseError<&str>>(
|
||||||
|
r#" 1. item1
|
||||||
|
2. item2
|
||||||
|
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,
|
||||||
|
ordered: false,
|
||||||
|
},
|
||||||
|
r#"1
|
||||||
|
|
||||||
|
- 2
|
||||||
|
|
||||||
|
- 3
|
||||||
|
|
||||||
|
"#
|
||||||
|
)
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 } => {
|
||||||
|
|
244
src/parsers.rs
244
src/parsers.rs
|
@ -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,109 +557,134 @@ 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;
|
||||||
containers.push(Container::Block { content, node });
|
|
||||||
contents = tail;
|
let parent = arena.append(Element::Document { pre_blank: 0 }, parent); // placeholder
|
||||||
|
|
||||||
|
let node = arena.append(first_item, parent);
|
||||||
|
containers.push(Container::Block { content, node });
|
||||||
|
|
||||||
|
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>>(
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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]
|
||||||
|
|
Loading…
Reference in a new issue