2019-08-06 07:03:16 +01:00
|
|
|
// parser related functions
|
2019-08-04 10:46:10 +01:00
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
use std::borrow::Cow;
|
2019-09-13 12:57:01 +01:00
|
|
|
use std::iter::once;
|
2019-08-10 13:17:39 +01:00
|
|
|
use std::marker::PhantomData;
|
2019-08-09 15:15:06 +01:00
|
|
|
|
2019-08-06 07:03:16 +01:00
|
|
|
use indextree::{Arena, NodeId};
|
2019-09-13 16:49:28 +01:00
|
|
|
use jetscii::{bytes, BytesConst};
|
2019-08-09 15:15:06 +01:00
|
|
|
use memchr::{memchr, memchr_iter};
|
2019-10-02 09:50:36 +01:00
|
|
|
use nom::{bytes::complete::take_while1, combinator::verify, error::ParseError, IResult};
|
2019-08-04 10:46:10 +01:00
|
|
|
|
2019-08-06 07:03:16 +01:00
|
|
|
use crate::config::ParseConfig;
|
2019-10-02 09:50:36 +01:00
|
|
|
use crate::elements::{
|
|
|
|
block::parse_block_element, emphasis::parse_emphasis, keyword::parse_keyword,
|
2019-10-30 06:43:55 +00:00
|
|
|
radio_target::parse_radio_target, BabelCall, CenterBlock, Clock, Comment, CommentBlock, Cookie,
|
|
|
|
Drawer, DynBlock, Element, ExampleBlock, ExportBlock, FixedWidth, FnDef, FnRef, InlineCall,
|
|
|
|
InlineSrc, Keyword, Link, List, ListItem, Macros, QuoteBlock, Rule, Snippet, SourceBlock,
|
|
|
|
SpecialBlock, Table, TableRow, Target, Timestamp, Title, VerseBlock,
|
2019-10-02 09:50:36 +01:00
|
|
|
};
|
2019-08-06 07:03:16 +01:00
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
pub trait ElementArena<'a> {
|
2019-10-30 03:31:37 +00:00
|
|
|
fn append<T>(&mut self, element: T, parent: NodeId) -> NodeId
|
|
|
|
where
|
|
|
|
T: Into<Element<'a>>;
|
|
|
|
fn insert_before_last_child<T>(&mut self, element: T, parent: NodeId) -> NodeId
|
|
|
|
where
|
|
|
|
T: Into<Element<'a>>;
|
|
|
|
fn set<T>(&mut self, node: NodeId, element: T)
|
|
|
|
where
|
|
|
|
T: Into<Element<'a>>;
|
2019-08-09 15:15:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> ElementArena<'a> for Arena<Element<'a>> {
|
2019-10-30 03:31:37 +00:00
|
|
|
fn append<T>(&mut self, element: T, parent: NodeId) -> NodeId
|
|
|
|
where
|
|
|
|
T: Into<Element<'a>>,
|
|
|
|
{
|
2019-08-09 15:15:06 +01:00
|
|
|
let node = self.new_node(element.into());
|
|
|
|
parent.append(node, self);
|
|
|
|
node
|
|
|
|
}
|
|
|
|
|
2019-10-30 03:31:37 +00:00
|
|
|
fn insert_before_last_child<T>(&mut self, element: T, parent: NodeId) -> NodeId
|
|
|
|
where
|
|
|
|
T: Into<Element<'a>>,
|
|
|
|
{
|
2019-08-09 15:15:06 +01:00
|
|
|
if let Some(child) = self[parent].last_child() {
|
|
|
|
let node = self.new_node(element.into());
|
|
|
|
child.insert_before(node, self);
|
|
|
|
node
|
|
|
|
} else {
|
2019-10-30 03:31:37 +00:00
|
|
|
self.append(element, parent)
|
2019-08-09 15:15:06 +01:00
|
|
|
}
|
|
|
|
}
|
2019-10-30 03:31:37 +00:00
|
|
|
|
|
|
|
fn set<T>(&mut self, node: NodeId, element: T)
|
|
|
|
where
|
|
|
|
T: Into<Element<'a>>,
|
|
|
|
{
|
|
|
|
*self[node].get_mut() = element.into();
|
|
|
|
}
|
2019-08-09 15:15:06 +01:00
|
|
|
}
|
|
|
|
|
2019-08-11 04:40:52 +01:00
|
|
|
pub struct OwnedArena<'a, 'b, 'c> {
|
|
|
|
arena: &'b mut Arena<Element<'c>>,
|
|
|
|
phantom: PhantomData<&'a ()>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 'b, 'c> OwnedArena<'a, 'b, 'c> {
|
|
|
|
pub fn new(arena: &'b mut Arena<Element<'c>>) -> OwnedArena<'a, 'b, 'c> {
|
|
|
|
OwnedArena {
|
|
|
|
arena,
|
|
|
|
phantom: PhantomData,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> ElementArena<'a> for OwnedArena<'a, '_, '_> {
|
2019-10-30 03:31:37 +00:00
|
|
|
fn append<T>(&mut self, element: T, parent: NodeId) -> NodeId
|
|
|
|
where
|
|
|
|
T: Into<Element<'a>>,
|
|
|
|
{
|
|
|
|
self.arena.append(element.into().into_owned(), parent)
|
2019-08-11 04:40:52 +01:00
|
|
|
}
|
|
|
|
|
2019-10-30 03:31:37 +00:00
|
|
|
fn insert_before_last_child<T>(&mut self, element: T, parent: NodeId) -> NodeId
|
|
|
|
where
|
|
|
|
T: Into<Element<'a>>,
|
|
|
|
{
|
2019-10-02 09:50:36 +01:00
|
|
|
self.arena
|
|
|
|
.insert_before_last_child(element.into().into_owned(), parent)
|
2019-08-11 04:40:52 +01:00
|
|
|
}
|
2019-10-30 03:31:37 +00:00
|
|
|
|
|
|
|
fn set<T>(&mut self, node: NodeId, element: T)
|
|
|
|
where
|
|
|
|
T: Into<Element<'a>>,
|
|
|
|
{
|
|
|
|
self.arena.set(node, element.into().into_owned());
|
|
|
|
}
|
2019-08-11 04:40:52 +01:00
|
|
|
}
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
#[derive(Debug)]
|
2019-08-06 07:03:16 +01:00
|
|
|
pub enum Container<'a> {
|
|
|
|
// Block, List Item
|
2019-10-30 03:31:37 +00:00
|
|
|
Block { content: &'a str, node: NodeId },
|
2019-08-06 07:03:16 +01:00
|
|
|
// Pargraph, Inline Markup
|
2019-10-30 03:31:37 +00:00
|
|
|
Inline { content: &'a str, node: NodeId },
|
2019-08-06 07:03:16 +01:00
|
|
|
// Headline
|
2019-10-30 03:31:37 +00:00
|
|
|
Headline { content: &'a str, node: NodeId },
|
2019-08-06 07:03:16 +01:00
|
|
|
// Document
|
2019-10-30 03:31:37 +00:00
|
|
|
Document { content: &'a str, node: NodeId },
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
|
2019-08-10 13:17:39 +01:00
|
|
|
pub fn parse_container<'a, T: ElementArena<'a>>(
|
|
|
|
arena: &mut T,
|
|
|
|
container: Container<'a>,
|
|
|
|
config: &ParseConfig,
|
|
|
|
) {
|
|
|
|
let containers = &mut vec![container];
|
|
|
|
|
|
|
|
while let Some(container) = containers.pop() {
|
|
|
|
match container {
|
|
|
|
Container::Document { content, node } => {
|
|
|
|
parse_section_and_headlines(arena, content, node, containers);
|
|
|
|
}
|
|
|
|
Container::Headline { content, node } => {
|
|
|
|
parse_headline_content(arena, content, node, containers, config);
|
|
|
|
}
|
|
|
|
Container::Block { content, node } => {
|
|
|
|
parse_blocks(arena, content, node, containers);
|
|
|
|
}
|
|
|
|
Container::Inline { content, node } => {
|
|
|
|
parse_inlines(arena, content, node, containers);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
pub fn parse_headline_content<'a, T: ElementArena<'a>>(
|
|
|
|
arena: &mut T,
|
2019-08-06 07:03:16 +01:00
|
|
|
content: &'a str,
|
|
|
|
parent: NodeId,
|
|
|
|
containers: &mut Vec<Container<'a>>,
|
|
|
|
config: &ParseConfig,
|
2019-08-09 15:15:06 +01:00
|
|
|
) {
|
2019-08-06 14:10:57 +01:00
|
|
|
let (tail, (title, content)) = Title::parse(content, config).unwrap();
|
2019-10-30 03:31:37 +00:00
|
|
|
let node = arena.append(title, parent);
|
2019-08-06 07:03:16 +01:00
|
|
|
containers.push(Container::Inline { content, node });
|
2019-08-09 15:15:06 +01:00
|
|
|
parse_section_and_headlines(arena, tail, parent, containers);
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
pub fn parse_section_and_headlines<'a, T: ElementArena<'a>>(
|
|
|
|
arena: &mut T,
|
2019-08-06 07:03:16 +01:00
|
|
|
content: &'a str,
|
|
|
|
parent: NodeId,
|
|
|
|
containers: &mut Vec<Container<'a>>,
|
|
|
|
) {
|
|
|
|
let content = skip_empty_lines(content);
|
|
|
|
if content.is_empty() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut last_end = 0;
|
2019-09-13 12:57:01 +01:00
|
|
|
for i in memchr_iter(b'\n', content.as_bytes()).chain(once(content.len())) {
|
2019-10-02 09:50:36 +01:00
|
|
|
if let Some((mut tail, (headline_content, level))) = parse_headline(&content[last_end..]) {
|
2019-08-06 07:03:16 +01:00
|
|
|
if last_end != 0 {
|
2019-10-30 03:31:37 +00:00
|
|
|
let node = arena.append(Element::Section, parent);
|
2019-08-09 15:15:06 +01:00
|
|
|
let content = &content[0..last_end];
|
|
|
|
containers.push(Container::Block { content, node });
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
2019-08-09 15:15:06 +01:00
|
|
|
|
2019-10-30 03:31:37 +00:00
|
|
|
let node = arena.append(Element::Headline { level }, parent);
|
2019-08-06 07:03:16 +01:00
|
|
|
containers.push(Container::Headline {
|
|
|
|
content: headline_content,
|
|
|
|
node,
|
|
|
|
});
|
2019-08-09 15:15:06 +01:00
|
|
|
|
2019-10-02 09:50:36 +01:00
|
|
|
while let Some((new_tail, (content, level))) = parse_headline(tail) {
|
2019-08-09 15:15:06 +01:00
|
|
|
debug_assert_ne!(tail, new_tail);
|
2019-10-30 03:31:37 +00:00
|
|
|
let node = arena.append(Element::Headline { level }, parent);
|
2019-08-06 07:03:16 +01:00
|
|
|
containers.push(Container::Headline { content, node });
|
|
|
|
tail = new_tail;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
last_end = i + 1;
|
|
|
|
}
|
|
|
|
|
2019-10-30 03:31:37 +00:00
|
|
|
let node = arena.append(Element::Section, parent);
|
2019-08-06 07:03:16 +01:00
|
|
|
containers.push(Container::Block { content, node });
|
|
|
|
}
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
pub fn parse_blocks<'a, T: ElementArena<'a>>(
|
|
|
|
arena: &mut T,
|
2019-08-06 07:03:16 +01:00
|
|
|
content: &'a str,
|
|
|
|
parent: NodeId,
|
|
|
|
containers: &mut Vec<Container<'a>>,
|
|
|
|
) {
|
|
|
|
let mut tail = skip_empty_lines(content);
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
if let Some(new_tail) = parse_block(content, arena, parent, containers) {
|
2019-08-06 07:03:16 +01:00
|
|
|
tail = skip_empty_lines(new_tail);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut text = tail;
|
|
|
|
let mut pos = 0;
|
|
|
|
|
|
|
|
while !tail.is_empty() {
|
|
|
|
let i = memchr(b'\n', tail.as_bytes())
|
|
|
|
.map(|i| i + 1)
|
|
|
|
.unwrap_or_else(|| tail.len());
|
|
|
|
if tail.as_bytes()[0..i].iter().all(u8::is_ascii_whitespace) {
|
2019-10-28 05:33:18 +00:00
|
|
|
let (tail_, blank) = blank_lines(&tail[i..]);
|
|
|
|
debug_assert_ne!(tail, tail_);
|
|
|
|
tail = tail_;
|
|
|
|
|
2019-10-30 03:31:37 +00:00
|
|
|
let node = arena.append(
|
2019-10-28 05:33:18 +00:00
|
|
|
Element::Paragraph {
|
|
|
|
// including current line (&tail[0..i])
|
|
|
|
post_blank: blank + 1,
|
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
2019-10-01 13:35:56 +01:00
|
|
|
|
|
|
|
containers.push(Container::Inline {
|
2019-10-28 05:33:18 +00:00
|
|
|
content: &text[0..pos].trim_end(),
|
2019-10-01 13:35:56 +01:00
|
|
|
node,
|
|
|
|
});
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
pos = 0;
|
2019-08-06 07:03:16 +01:00
|
|
|
text = tail;
|
2019-08-09 15:15:06 +01:00
|
|
|
} else if let Some(new_tail) = parse_block(tail, arena, parent, containers) {
|
2019-08-06 07:03:16 +01:00
|
|
|
if pos != 0 {
|
2019-10-28 05:33:18 +00:00
|
|
|
let node =
|
|
|
|
arena.insert_before_last_child(Element::Paragraph { post_blank: 0 }, parent);
|
2019-10-01 13:35:56 +01:00
|
|
|
|
|
|
|
containers.push(Container::Inline {
|
2019-10-28 05:33:18 +00:00
|
|
|
content: &text[0..pos].trim_end(),
|
2019-10-01 13:35:56 +01:00
|
|
|
node,
|
|
|
|
});
|
|
|
|
|
2019-08-06 07:03:16 +01:00
|
|
|
pos = 0;
|
|
|
|
}
|
2019-10-01 13:35:56 +01:00
|
|
|
debug_assert_ne!(tail, skip_empty_lines(new_tail));
|
2019-08-06 07:03:16 +01:00
|
|
|
tail = skip_empty_lines(new_tail);
|
|
|
|
text = tail;
|
|
|
|
} else {
|
2019-08-09 15:15:06 +01:00
|
|
|
debug_assert_ne!(tail, &tail[i..]);
|
2019-08-06 07:03:16 +01:00
|
|
|
tail = &tail[i..];
|
|
|
|
pos += i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !text.is_empty() {
|
2019-10-30 03:31:37 +00:00
|
|
|
let node = arena.append(Element::Paragraph { post_blank: 0 }, parent);
|
2019-10-01 13:35:56 +01:00
|
|
|
|
|
|
|
containers.push(Container::Inline {
|
2019-10-28 05:33:18 +00:00
|
|
|
content: &text[0..pos].trim_end(),
|
2019-10-01 13:35:56 +01:00
|
|
|
node,
|
|
|
|
});
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
pub fn parse_block<'a, T: ElementArena<'a>>(
|
2019-08-06 07:03:16 +01:00
|
|
|
contents: &'a str,
|
2019-08-09 15:15:06 +01:00
|
|
|
arena: &mut T,
|
|
|
|
parent: NodeId,
|
2019-08-06 07:03:16 +01:00
|
|
|
containers: &mut Vec<Container<'a>>,
|
2019-08-09 15:15:06 +01:00
|
|
|
) -> Option<&'a str> {
|
2019-10-30 06:43:55 +00:00
|
|
|
match contents
|
|
|
|
.as_bytes()
|
|
|
|
.iter()
|
|
|
|
.find(|c| !c.is_ascii_whitespace())?
|
|
|
|
{
|
|
|
|
b'[' => {
|
|
|
|
let (tail, (fn_def, content)) = FnDef::parse(contents)?;
|
|
|
|
let node = arena.append(fn_def, parent);
|
|
|
|
containers.push(Container::Block { content, node });
|
|
|
|
Some(tail)
|
|
|
|
}
|
|
|
|
b'0'..=b'9' | b'*' => {
|
|
|
|
let tail = parse_list(arena, contents, parent, containers)?;
|
|
|
|
Some(tail)
|
|
|
|
}
|
2019-08-09 15:15:06 +01:00
|
|
|
b'C' => {
|
2019-10-02 09:50:36 +01:00
|
|
|
let (tail, clock) = Clock::parse(contents)?;
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(clock, parent);
|
2019-10-02 09:50:36 +01:00
|
|
|
Some(tail)
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
2019-08-09 15:15:06 +01:00
|
|
|
b'\'' => {
|
|
|
|
// TODO: LaTeX environment
|
2019-10-02 09:50:36 +01:00
|
|
|
None
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
2019-08-09 15:15:06 +01:00
|
|
|
b'-' => {
|
2019-10-30 06:43:55 +00:00
|
|
|
if let Some((tail, rule)) = Rule::parse(contents) {
|
|
|
|
arena.append(rule, parent);
|
|
|
|
Some(tail)
|
|
|
|
} else {
|
|
|
|
let tail = parse_list(arena, contents, parent, containers)?;
|
|
|
|
Some(tail)
|
|
|
|
}
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
2019-08-09 15:15:06 +01:00
|
|
|
b':' => {
|
2019-10-02 09:50:36 +01:00
|
|
|
if let Some((tail, (drawer, content))) = Drawer::parse(contents) {
|
2019-10-30 03:31:37 +00:00
|
|
|
let node = arena.append(drawer, parent);
|
2019-08-09 15:15:06 +01:00
|
|
|
containers.push(Container::Block { content, node });
|
2019-10-02 09:50:36 +01:00
|
|
|
Some(tail)
|
|
|
|
} else {
|
2019-10-28 05:33:18 +00:00
|
|
|
let (tail, fixed_width) = FixedWidth::parse(contents)?;
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(fixed_width, parent);
|
2019-10-02 09:50:36 +01:00
|
|
|
Some(tail)
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
}
|
2019-08-09 15:15:06 +01:00
|
|
|
b'|' => {
|
2019-10-30 06:43:55 +00:00
|
|
|
let tail = parse_org_table(arena, contents, containers, parent);
|
2019-10-02 09:50:36 +01:00
|
|
|
Some(tail)
|
2019-08-09 15:15:06 +01:00
|
|
|
}
|
2019-10-30 06:43:55 +00:00
|
|
|
b'+' => {
|
|
|
|
if let Some((tail, table)) = Table::parse_table_el(contents) {
|
|
|
|
arena.append(table, parent);
|
|
|
|
Some(tail)
|
|
|
|
} else {
|
|
|
|
let tail = parse_list(arena, contents, parent, containers)?;
|
|
|
|
Some(tail)
|
|
|
|
}
|
|
|
|
}
|
2019-08-09 15:15:06 +01:00
|
|
|
b'#' => {
|
2019-10-28 05:33:18 +00:00
|
|
|
if let Some((tail, (name, args, content, blank))) = parse_block_element(contents) {
|
2019-08-09 15:15:06 +01:00
|
|
|
match_block(
|
|
|
|
arena,
|
|
|
|
parent,
|
|
|
|
containers,
|
|
|
|
name.into(),
|
|
|
|
args.map(Into::into),
|
|
|
|
content,
|
2019-10-28 05:33:18 +00:00
|
|
|
blank,
|
2019-08-09 15:15:06 +01:00
|
|
|
);
|
2019-10-02 09:50:36 +01:00
|
|
|
Some(tail)
|
|
|
|
} else if let Some((tail, (dyn_block, content))) = DynBlock::parse(contents) {
|
2019-10-30 03:31:37 +00:00
|
|
|
let node = arena.append(dyn_block, parent);
|
2019-08-09 15:15:06 +01:00
|
|
|
containers.push(Container::Block { content, node });
|
2019-10-02 09:50:36 +01:00
|
|
|
Some(tail)
|
2019-10-28 05:33:18 +00:00
|
|
|
} else if let Some((tail, (key, optional, value, blank))) = parse_keyword(contents) {
|
2019-08-09 15:15:06 +01:00
|
|
|
if (&*key).eq_ignore_ascii_case("CALL") {
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(
|
2019-08-09 15:15:06 +01:00
|
|
|
BabelCall {
|
|
|
|
value: value.into(),
|
2019-10-28 05:33:18 +00:00
|
|
|
post_blank: blank,
|
2019-08-09 15:15:06 +01:00
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
|
|
|
} else {
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(
|
2019-08-09 15:15:06 +01:00
|
|
|
Keyword {
|
|
|
|
key: key.into(),
|
|
|
|
optional: optional.map(Into::into),
|
|
|
|
value: value.into(),
|
2019-10-28 05:33:18 +00:00
|
|
|
post_blank: blank,
|
2019-08-09 15:15:06 +01:00
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
|
|
|
}
|
2019-10-02 09:50:36 +01:00
|
|
|
Some(tail)
|
|
|
|
} else {
|
2019-10-28 05:33:18 +00:00
|
|
|
let (tail, comment) = Comment::parse(contents)?;
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(comment, parent);
|
2019-10-02 09:50:36 +01:00
|
|
|
Some(tail)
|
2019-08-09 15:15:06 +01:00
|
|
|
}
|
|
|
|
}
|
2019-10-02 09:50:36 +01:00
|
|
|
_ => None,
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
2019-08-09 15:15:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn match_block<'a, T: ElementArena<'a>>(
|
|
|
|
arena: &mut T,
|
|
|
|
parent: NodeId,
|
|
|
|
containers: &mut Vec<Container<'a>>,
|
|
|
|
name: Cow<'a, str>,
|
2019-10-27 03:40:01 +00:00
|
|
|
parameters: Option<Cow<'a, str>>,
|
2019-08-09 15:15:06 +01:00
|
|
|
content: &'a str,
|
2019-10-28 05:33:18 +00:00
|
|
|
post_blank: usize,
|
2019-08-09 15:15:06 +01:00
|
|
|
) {
|
|
|
|
match &*name.to_uppercase() {
|
|
|
|
"CENTER" => {
|
2019-10-28 05:33:18 +00:00
|
|
|
let (content, pre_blank) = blank_lines(content);
|
2019-10-30 03:31:37 +00:00
|
|
|
let node = arena.append(
|
2019-10-28 05:33:18 +00:00
|
|
|
CenterBlock {
|
|
|
|
parameters,
|
|
|
|
pre_blank,
|
|
|
|
post_blank,
|
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
2019-08-09 15:15:06 +01:00
|
|
|
containers.push(Container::Block { content, node });
|
|
|
|
}
|
|
|
|
"QUOTE" => {
|
2019-10-28 05:33:18 +00:00
|
|
|
let (content, pre_blank) = blank_lines(content);
|
2019-10-30 03:31:37 +00:00
|
|
|
let node = arena.append(
|
2019-10-28 05:33:18 +00:00
|
|
|
QuoteBlock {
|
|
|
|
parameters,
|
|
|
|
pre_blank,
|
|
|
|
post_blank,
|
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
|
|
|
containers.push(Container::Block { content, node });
|
|
|
|
}
|
|
|
|
"VERSE" => {
|
|
|
|
let (content, pre_blank) = blank_lines(content);
|
2019-10-30 03:31:37 +00:00
|
|
|
let node = arena.append(
|
2019-10-28 05:33:18 +00:00
|
|
|
VerseBlock {
|
|
|
|
parameters,
|
|
|
|
pre_blank,
|
|
|
|
post_blank,
|
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
2019-08-09 15:15:06 +01:00
|
|
|
containers.push(Container::Block { content, node });
|
|
|
|
}
|
|
|
|
"COMMENT" => {
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(
|
2019-08-09 15:15:06 +01:00
|
|
|
CommentBlock {
|
2019-10-27 03:40:01 +00:00
|
|
|
data: parameters,
|
2019-08-09 15:15:06 +01:00
|
|
|
contents: content.into(),
|
2019-10-28 05:33:18 +00:00
|
|
|
post_blank,
|
2019-08-09 15:15:06 +01:00
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
"EXAMPLE" => {
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(
|
2019-08-09 15:15:06 +01:00
|
|
|
ExampleBlock {
|
2019-10-27 03:40:01 +00:00
|
|
|
data: parameters,
|
2019-08-09 15:15:06 +01:00
|
|
|
contents: content.into(),
|
2019-10-28 05:33:18 +00:00
|
|
|
post_blank,
|
2019-08-09 15:15:06 +01:00
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
"EXPORT" => {
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(
|
2019-08-09 15:15:06 +01:00
|
|
|
ExportBlock {
|
2019-10-27 03:40:01 +00:00
|
|
|
data: parameters.unwrap_or_default(),
|
2019-08-09 15:15:06 +01:00
|
|
|
contents: content.into(),
|
2019-10-28 05:33:18 +00:00
|
|
|
post_blank,
|
2019-08-09 15:15:06 +01:00
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
"SRC" => {
|
2019-10-27 03:40:01 +00:00
|
|
|
let (language, arguments) = match ¶meters {
|
2019-08-09 15:15:06 +01:00
|
|
|
Some(Cow::Borrowed(args)) => {
|
|
|
|
let (language, arguments) =
|
|
|
|
args.split_at(args.find(' ').unwrap_or_else(|| args.len()));
|
|
|
|
(language.into(), arguments.into())
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
2019-08-09 15:15:06 +01:00
|
|
|
None => (Cow::Borrowed(""), Cow::Borrowed("")),
|
|
|
|
_ => unreachable!("`parse_block_element` returns `Some(Cow::Borrowed)` or `None`"),
|
|
|
|
};
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(
|
2019-08-09 15:15:06 +01:00
|
|
|
SourceBlock {
|
|
|
|
arguments,
|
|
|
|
language,
|
|
|
|
contents: content.into(),
|
2019-10-28 05:33:18 +00:00
|
|
|
post_blank,
|
2019-08-09 15:15:06 +01:00
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
_ => {
|
2019-10-28 05:33:18 +00:00
|
|
|
let (content, pre_blank) = blank_lines(content);
|
2019-10-30 03:31:37 +00:00
|
|
|
let node = arena.append(
|
2019-10-28 05:33:18 +00:00
|
|
|
SpecialBlock {
|
|
|
|
parameters,
|
|
|
|
name,
|
|
|
|
pre_blank,
|
|
|
|
post_blank,
|
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
2019-08-06 07:03:16 +01:00
|
|
|
containers.push(Container::Block { content, node });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-06 04:42:44 +01:00
|
|
|
struct InlinePositions<'a> {
|
|
|
|
bytes: &'a [u8],
|
2019-10-27 03:40:01 +00:00
|
|
|
pos: usize,
|
2019-10-06 04:42:44 +01:00
|
|
|
next: Option<usize>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl InlinePositions<'_> {
|
2019-10-09 08:56:25 +01:00
|
|
|
fn new(bytes: &[u8]) -> InlinePositions {
|
2019-10-06 04:42:44 +01:00
|
|
|
InlinePositions {
|
|
|
|
bytes,
|
2019-10-27 03:40:01 +00:00
|
|
|
pos: 0,
|
2019-10-06 04:42:44 +01:00
|
|
|
next: Some(0),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Iterator for InlinePositions<'_> {
|
|
|
|
type Item = usize;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
lazy_static::lazy_static! {
|
|
|
|
static ref PRE_BYTES: BytesConst =
|
|
|
|
bytes!(b'@', b'<', b'[', b' ', b'(', b'{', b'\'', b'"', b'\n');
|
|
|
|
}
|
|
|
|
|
|
|
|
self.next.take().or_else(|| {
|
2019-10-27 03:40:01 +00:00
|
|
|
PRE_BYTES.find(&self.bytes[self.pos..]).map(|i| {
|
|
|
|
self.pos += i + 1;
|
2019-10-06 04:42:44 +01:00
|
|
|
|
2019-10-27 03:40:01 +00:00
|
|
|
match self.bytes[self.pos - 1] {
|
2019-10-06 04:42:44 +01:00
|
|
|
b'{' => {
|
2019-10-27 03:40:01 +00:00
|
|
|
self.next = Some(self.pos);
|
|
|
|
self.pos - 1
|
2019-10-06 04:42:44 +01:00
|
|
|
}
|
2019-10-27 03:40:01 +00:00
|
|
|
b' ' | b'(' | b'\'' | b'"' | b'\n' => self.pos,
|
|
|
|
_ => self.pos - 1,
|
2019-10-06 04:42:44 +01:00
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
pub fn parse_inlines<'a, T: ElementArena<'a>>(
|
|
|
|
arena: &mut T,
|
2019-08-06 07:03:16 +01:00
|
|
|
content: &'a str,
|
|
|
|
parent: NodeId,
|
|
|
|
containers: &mut Vec<Container<'a>>,
|
|
|
|
) {
|
|
|
|
let mut tail = content;
|
|
|
|
|
2019-10-06 04:42:44 +01:00
|
|
|
if let Some(tail_) = parse_inline(tail, arena, containers, parent) {
|
|
|
|
tail = tail_;
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
|
2019-10-06 04:42:44 +01:00
|
|
|
while let Some((tail_, i)) = InlinePositions::new(tail.as_bytes())
|
|
|
|
.filter_map(|i| parse_inline(&tail[i..], arena, containers, parent).map(|tail| (tail, i)))
|
|
|
|
.next()
|
|
|
|
{
|
|
|
|
if i != 0 {
|
2019-08-10 13:17:39 +01:00
|
|
|
arena.insert_before_last_child(
|
|
|
|
Element::Text {
|
2019-10-06 04:42:44 +01:00
|
|
|
value: tail[0..i].into(),
|
2019-08-10 13:17:39 +01:00
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
2019-10-06 04:42:44 +01:00
|
|
|
tail = tail_;
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
|
2019-10-06 04:42:44 +01:00
|
|
|
if !tail.is_empty() {
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(Element::Text { value: tail.into() }, parent);
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
pub fn parse_inline<'a, T: ElementArena<'a>>(
|
2019-08-06 07:03:16 +01:00
|
|
|
contents: &'a str,
|
2019-08-09 15:15:06 +01:00
|
|
|
arena: &mut T,
|
2019-08-06 07:03:16 +01:00
|
|
|
containers: &mut Vec<Container<'a>>,
|
2019-08-09 15:15:06 +01:00
|
|
|
parent: NodeId,
|
|
|
|
) -> Option<&'a str> {
|
2019-08-06 07:03:16 +01:00
|
|
|
if contents.len() < 3 {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2019-10-02 09:50:36 +01:00
|
|
|
match contents.as_bytes()[0] {
|
2019-08-09 15:15:06 +01:00
|
|
|
b'@' => {
|
2019-10-02 09:50:36 +01:00
|
|
|
let (tail, snippet) = Snippet::parse(contents)?;
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(snippet, parent);
|
2019-10-02 09:50:36 +01:00
|
|
|
Some(tail)
|
2019-08-09 15:15:06 +01:00
|
|
|
}
|
|
|
|
b'{' => {
|
2019-10-02 09:50:36 +01:00
|
|
|
let (tail, macros) = Macros::parse(contents)?;
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(macros, parent);
|
2019-10-02 09:50:36 +01:00
|
|
|
Some(tail)
|
2019-08-09 15:15:06 +01:00
|
|
|
}
|
|
|
|
b'<' => {
|
2019-10-02 09:50:36 +01:00
|
|
|
if let Some((tail, _content)) = parse_radio_target(contents) {
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(Element::RadioTarget, parent);
|
2019-10-02 09:50:36 +01:00
|
|
|
Some(tail)
|
|
|
|
} else if let Some((tail, target)) = Target::parse(contents) {
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(target, parent);
|
2019-10-02 09:50:36 +01:00
|
|
|
Some(tail)
|
|
|
|
} else if let Some((tail, timestamp)) = Timestamp::parse_active(contents) {
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(timestamp, parent);
|
2019-10-02 09:50:36 +01:00
|
|
|
Some(tail)
|
|
|
|
} else {
|
|
|
|
let (tail, timestamp) = Timestamp::parse_diary(contents)?;
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(timestamp, parent);
|
2019-10-02 09:50:36 +01:00
|
|
|
Some(tail)
|
2019-08-09 15:15:06 +01:00
|
|
|
}
|
|
|
|
}
|
2019-08-06 07:03:16 +01:00
|
|
|
b'[' => {
|
2019-10-02 09:50:36 +01:00
|
|
|
if let Some((tail, fn_ref)) = FnRef::parse(contents) {
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(fn_ref, parent);
|
2019-10-02 09:50:36 +01:00
|
|
|
Some(tail)
|
|
|
|
} else if let Some((tail, link)) = Link::parse(contents) {
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(link, parent);
|
2019-10-02 09:50:36 +01:00
|
|
|
Some(tail)
|
|
|
|
} else if let Some((tail, cookie)) = Cookie::parse(contents) {
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(cookie, parent);
|
2019-10-02 09:50:36 +01:00
|
|
|
Some(tail)
|
|
|
|
} else {
|
|
|
|
let (tail, timestamp) = Timestamp::parse_inactive(contents)?;
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(timestamp, parent);
|
2019-10-02 09:50:36 +01:00
|
|
|
Some(tail)
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
b'*' => {
|
2019-10-02 09:50:36 +01:00
|
|
|
let (tail, content) = parse_emphasis(contents, b'*')?;
|
2019-10-30 03:31:37 +00:00
|
|
|
let node = arena.append(Element::Bold, parent);
|
2019-10-02 09:50:36 +01:00
|
|
|
containers.push(Container::Inline { content, node });
|
|
|
|
Some(tail)
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
b'+' => {
|
2019-10-02 09:50:36 +01:00
|
|
|
let (tail, content) = parse_emphasis(contents, b'+')?;
|
2019-10-30 03:31:37 +00:00
|
|
|
let node = arena.append(Element::Strike, parent);
|
2019-10-02 09:50:36 +01:00
|
|
|
containers.push(Container::Inline { content, node });
|
|
|
|
Some(tail)
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
b'/' => {
|
2019-10-02 09:50:36 +01:00
|
|
|
let (tail, content) = parse_emphasis(contents, b'/')?;
|
2019-10-30 03:31:37 +00:00
|
|
|
let node = arena.append(Element::Italic, parent);
|
2019-10-02 09:50:36 +01:00
|
|
|
containers.push(Container::Inline { content, node });
|
|
|
|
Some(tail)
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
b'_' => {
|
2019-10-02 09:50:36 +01:00
|
|
|
let (tail, content) = parse_emphasis(contents, b'_')?;
|
2019-10-30 03:31:37 +00:00
|
|
|
let node = arena.append(Element::Underline, parent);
|
2019-10-02 09:50:36 +01:00
|
|
|
containers.push(Container::Inline { content, node });
|
|
|
|
Some(tail)
|
2019-08-09 15:15:06 +01:00
|
|
|
}
|
|
|
|
b'=' => {
|
2019-10-02 09:50:36 +01:00
|
|
|
let (tail, value) = parse_emphasis(contents, b'=')?;
|
|
|
|
let value = value.into();
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(Element::Verbatim { value }, parent);
|
2019-10-02 09:50:36 +01:00
|
|
|
Some(tail)
|
2019-08-09 15:15:06 +01:00
|
|
|
}
|
|
|
|
b'~' => {
|
2019-10-02 09:50:36 +01:00
|
|
|
let (tail, value) = parse_emphasis(contents, b'~')?;
|
|
|
|
let value = value.into();
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(Element::Code { value }, parent);
|
2019-10-02 09:50:36 +01:00
|
|
|
Some(tail)
|
2019-08-09 15:15:06 +01:00
|
|
|
}
|
|
|
|
b's' => {
|
2019-10-02 09:50:36 +01:00
|
|
|
let (tail, inline_src) = InlineSrc::parse(contents)?;
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(inline_src, parent);
|
2019-10-02 09:50:36 +01:00
|
|
|
Some(tail)
|
2019-08-09 15:15:06 +01:00
|
|
|
}
|
|
|
|
b'c' => {
|
2019-10-02 09:50:36 +01:00
|
|
|
let (tail, inline_call) = InlineCall::parse(contents)?;
|
2019-10-30 03:31:37 +00:00
|
|
|
arena.append(inline_call, parent);
|
2019-10-02 09:50:36 +01:00
|
|
|
Some(tail)
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
2019-10-02 09:50:36 +01:00
|
|
|
_ => None,
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-30 03:31:37 +00:00
|
|
|
pub fn parse_list<'a, T: ElementArena<'a>>(
|
2019-08-09 15:15:06 +01:00
|
|
|
arena: &mut T,
|
2019-10-30 03:31:37 +00:00
|
|
|
contents: &'a str,
|
2019-08-06 07:03:16 +01:00
|
|
|
parent: NodeId,
|
|
|
|
containers: &mut Vec<Container<'a>>,
|
2019-10-30 03:31:37 +00:00
|
|
|
) -> Option<&'a str> {
|
|
|
|
let (mut tail, (first_item, content)) = ListItem::parse(contents)?;
|
|
|
|
let first_item_indent = first_item.indent;
|
|
|
|
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 });
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
2019-10-30 03:31:37 +00:00
|
|
|
|
|
|
|
let (tail, blank) = blank_lines(tail);
|
|
|
|
|
|
|
|
arena.set(
|
|
|
|
parent,
|
|
|
|
List {
|
|
|
|
indent: first_item_indent,
|
|
|
|
ordered: first_item_ordered,
|
|
|
|
post_blank: blank,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
Some(tail)
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
|
2019-10-30 06:43:55 +00:00
|
|
|
pub fn parse_org_table<'a, T: ElementArena<'a>>(
|
2019-08-09 15:15:06 +01:00
|
|
|
arena: &mut T,
|
2019-08-06 07:03:16 +01:00
|
|
|
contents: &'a str,
|
|
|
|
containers: &mut Vec<Container<'a>>,
|
2019-08-09 15:15:06 +01:00
|
|
|
parent: NodeId,
|
2019-10-30 06:43:55 +00:00
|
|
|
) -> &'a str {
|
|
|
|
let (tail, contents) = take_lines_while(|line| line.trim_start().starts_with('|'))(contents);
|
|
|
|
let (tail, blank) = blank_lines(tail);
|
2019-08-06 07:03:16 +01:00
|
|
|
|
2019-10-30 06:43:55 +00:00
|
|
|
let parent = arena.append(
|
|
|
|
Table::Org {
|
|
|
|
tblfm: None,
|
|
|
|
post_blank: blank,
|
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
2019-10-02 09:50:36 +01:00
|
|
|
|
2019-10-30 06:43:55 +00:00
|
|
|
let mut last_end = 0;
|
|
|
|
for start in memchr_iter(b'\n', contents.as_bytes()).chain(once(contents.len())) {
|
|
|
|
let line = contents[last_end..start].trim_start();
|
|
|
|
if line.starts_with("|-") {
|
|
|
|
arena.append(TableRow::Rule, parent);
|
|
|
|
} else {
|
|
|
|
let parent = arena.append(TableRow::Standard, parent);
|
|
|
|
for content in line.split_terminator('|').skip(1) {
|
|
|
|
let node = arena.append(Element::TableCell, parent);
|
|
|
|
containers.push(Container::Inline { content, node });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
last_end = start + 1;
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
2019-10-30 06:43:55 +00:00
|
|
|
|
|
|
|
tail
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
|
2019-10-09 08:56:25 +01:00
|
|
|
pub fn line<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, &str, E> {
|
2019-08-13 07:38:06 +01:00
|
|
|
if let Some(i) = memchr(b'\n', input.as_bytes()) {
|
|
|
|
if i > 0 && input.as_bytes()[i - 1] == b'\r' {
|
|
|
|
Ok((&input[i + 1..], &input[0..i - 1]))
|
|
|
|
} else {
|
|
|
|
Ok((&input[i + 1..], &input[0..i]))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Ok(("", input))
|
|
|
|
}
|
2019-08-04 10:46:10 +01:00
|
|
|
}
|
|
|
|
|
2019-10-09 08:56:25 +01:00
|
|
|
pub fn eol<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, &str, E> {
|
2019-10-27 03:40:01 +00:00
|
|
|
verify(line, |s: &str| {
|
|
|
|
s.as_bytes().iter().all(|c| c.is_ascii_whitespace())
|
|
|
|
})(input)
|
2019-08-09 15:15:06 +01:00
|
|
|
}
|
|
|
|
|
2019-10-02 09:50:36 +01:00
|
|
|
pub fn take_lines_while(predicate: impl Fn(&str) -> bool) -> impl Fn(&str) -> (&str, &str) {
|
2019-08-09 15:15:06 +01:00
|
|
|
move |input| {
|
2019-08-13 07:38:06 +01:00
|
|
|
let mut last_end = 0;
|
|
|
|
for i in memchr_iter(b'\n', input.as_bytes()) {
|
|
|
|
if i > 0 && input.as_bytes()[i - 1] == b'\r' {
|
|
|
|
if !predicate(&input[last_end..i - 1]) {
|
2019-10-02 09:50:36 +01:00
|
|
|
return (&input[last_end..], &input[0..last_end]);
|
2019-08-09 15:15:06 +01:00
|
|
|
}
|
2019-09-13 12:57:01 +01:00
|
|
|
} else if !predicate(&input[last_end..i]) {
|
2019-10-02 09:50:36 +01:00
|
|
|
return (&input[last_end..], &input[0..last_end]);
|
2019-08-13 07:38:06 +01:00
|
|
|
}
|
|
|
|
last_end = i + 1;
|
|
|
|
}
|
|
|
|
if !predicate(&input[last_end..]) {
|
2019-10-02 09:50:36 +01:00
|
|
|
(&input[last_end..], &input[0..last_end])
|
2019-08-13 07:38:06 +01:00
|
|
|
} else {
|
2019-10-02 09:50:36 +01:00
|
|
|
("", input)
|
2019-08-13 07:38:06 +01:00
|
|
|
}
|
2019-08-09 15:15:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn skip_empty_lines(input: &str) -> &str {
|
2019-10-27 03:40:01 +00:00
|
|
|
take_lines_while(|line| line.as_bytes().iter().all(|c| c.is_ascii_whitespace()))(input).0
|
2019-08-09 15:15:06 +01:00
|
|
|
}
|
|
|
|
|
2019-10-02 09:50:36 +01:00
|
|
|
pub fn parse_headline(input: &str) -> Option<(&str, (&str, usize))> {
|
2019-08-11 04:40:52 +01:00
|
|
|
let (input_, level) = parse_headline_level(input)?;
|
|
|
|
let (input_, content) = take_lines_while(move |line| {
|
2019-10-02 09:50:36 +01:00
|
|
|
parse_headline_level(line)
|
|
|
|
.map(|(_, l)| l > level)
|
|
|
|
.unwrap_or(true)
|
|
|
|
})(input_);
|
|
|
|
Some((input_, (&input[0..level + content.len()], level)))
|
2019-08-09 15:15:06 +01:00
|
|
|
}
|
2019-08-04 10:46:10 +01:00
|
|
|
|
2019-10-02 09:50:36 +01:00
|
|
|
pub fn parse_headline_level(input: &str) -> Option<(&str, usize)> {
|
|
|
|
let (input, stars) = take_while1::<_, _, ()>(|c: char| c == '*')(input).ok()?;
|
|
|
|
|
2019-08-13 07:38:06 +01:00
|
|
|
if input.starts_with(' ') || input.starts_with('\n') || input.is_empty() {
|
2019-10-02 09:50:36 +01:00
|
|
|
Some((input, stars.len()))
|
2019-08-09 15:15:06 +01:00
|
|
|
} else {
|
2019-10-02 09:50:36 +01:00
|
|
|
None
|
2019-08-04 10:46:10 +01:00
|
|
|
}
|
|
|
|
}
|
2019-08-05 15:23:32 +01:00
|
|
|
|
2019-10-09 08:56:25 +01:00
|
|
|
pub fn take_one_word<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, &str, E> {
|
2019-08-09 15:15:06 +01:00
|
|
|
take_while1(|c: char| !c.is_ascii_whitespace())(input)
|
2019-08-05 15:23:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2019-08-10 13:17:39 +01:00
|
|
|
pub fn test_skip_empty_lines() {
|
2019-08-05 15:23:32 +01:00
|
|
|
assert_eq!(skip_empty_lines("foo"), "foo");
|
|
|
|
assert_eq!(skip_empty_lines(" foo"), " foo");
|
|
|
|
assert_eq!(skip_empty_lines(" \nfoo\n"), "foo\n");
|
|
|
|
assert_eq!(skip_empty_lines(" \n\n\nfoo\n"), "foo\n");
|
|
|
|
assert_eq!(skip_empty_lines(" \n \n\nfoo\n"), "foo\n");
|
|
|
|
assert_eq!(skip_empty_lines(" \n \n\n foo\n"), " foo\n");
|
|
|
|
}
|
2019-10-28 05:33:18 +00:00
|
|
|
|
|
|
|
pub fn blank_lines(input: &str) -> (&str, usize) {
|
|
|
|
let bytes = input.as_bytes();
|
|
|
|
let mut blank = 0;
|
|
|
|
let mut last_end = 0;
|
|
|
|
for i in memchr_iter(b'\n', bytes) {
|
|
|
|
if bytes[last_end..i].iter().all(u8::is_ascii_whitespace) {
|
|
|
|
blank += 1;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
last_end = 1 + i;
|
|
|
|
}
|
|
|
|
(&input[last_end..], blank)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn test_blank_lines() {
|
|
|
|
assert_eq!(blank_lines("foo"), ("foo", 0));
|
|
|
|
assert_eq!(blank_lines(" foo"), (" foo", 0));
|
|
|
|
assert_eq!(blank_lines(" \t\nfoo\n"), ("foo\n", 1));
|
|
|
|
assert_eq!(blank_lines("\n \r\n\nfoo\n"), ("foo\n", 3));
|
|
|
|
assert_eq!(blank_lines("\r\n \n \r\n foo\n"), (" foo\n", 3));
|
|
|
|
}
|