
This ensures the parsers can take into account the affiliated keywords when setting their source without needing the SetSource trait.
125 lines
4.3 KiB
Rust
125 lines
4.3 KiB
Rust
use nom::branch::alt;
|
|
use nom::bytes::complete::tag;
|
|
use nom::bytes::complete::tag_no_case;
|
|
use nom::bytes::complete::take_while;
|
|
use nom::character::complete::line_ending;
|
|
use nom::character::complete::space0;
|
|
use nom::combinator::eof;
|
|
use nom::combinator::not;
|
|
use nom::combinator::recognize;
|
|
use nom::multi::many0;
|
|
use nom::multi::many_till;
|
|
use nom::sequence::tuple;
|
|
|
|
use super::keyword::affiliated_keyword;
|
|
use super::org_source::OrgSource;
|
|
use super::util::get_name;
|
|
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
|
use crate::context::parser_with_context;
|
|
use crate::context::ContextElement;
|
|
use crate::context::ExitClass;
|
|
use crate::context::ExitMatcherNode;
|
|
use crate::context::RefContext;
|
|
use crate::error::CustomError;
|
|
use crate::error::MyError;
|
|
use crate::error::Res;
|
|
use crate::parser::element_parser::element;
|
|
use crate::parser::util::blank_line;
|
|
use crate::parser::util::exit_matcher_parser;
|
|
use crate::parser::util::get_consumed;
|
|
use crate::parser::util::immediate_in_section;
|
|
use crate::parser::util::start_of_line;
|
|
use crate::parser::util::WORD_CONSTITUENT_CHARACTERS;
|
|
use crate::types::Drawer;
|
|
use crate::types::Element;
|
|
use crate::types::Paragraph;
|
|
use crate::types::SetSource;
|
|
|
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
pub(crate) fn drawer<'b, 'g, 'r, 's>(
|
|
context: RefContext<'b, 'g, 'r, 's>,
|
|
input: OrgSource<'s>,
|
|
) -> Res<OrgSource<'s>, Drawer<'s>> {
|
|
if immediate_in_section(context, "drawer") {
|
|
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
|
"Cannot nest objects of the same element".into(),
|
|
))));
|
|
}
|
|
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
|
start_of_line(remaining)?;
|
|
let (remaining, _leading_whitespace) = space0(remaining)?;
|
|
let (remaining, (_open_colon, drawer_name, _close_colon, _new_line)) = tuple((
|
|
tag(":"),
|
|
name,
|
|
tag(":"),
|
|
recognize(tuple((space0, line_ending))),
|
|
))(remaining)?;
|
|
|
|
let contexts = [
|
|
ContextElement::ConsumeTrailingWhitespace(true),
|
|
ContextElement::Context("drawer"),
|
|
ContextElement::ExitMatcherNode(ExitMatcherNode {
|
|
class: ExitClass::Alpha,
|
|
exit_matcher: &drawer_end,
|
|
}),
|
|
];
|
|
let parser_context = context.with_additional_node(&contexts[0]);
|
|
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
|
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
|
|
|
let element_matcher = parser_with_context!(element(true))(&parser_context);
|
|
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
|
let (remaining, children) = match tuple((
|
|
not(exit_matcher),
|
|
blank_line,
|
|
many_till(blank_line, exit_matcher),
|
|
))(remaining)
|
|
{
|
|
Ok((remain, (_not_immediate_exit, first_line, (_trailing_whitespace, _exit_contents)))) => {
|
|
let mut element = Element::Paragraph(Paragraph::of_text(first_line.into()));
|
|
let source = get_consumed(remaining, remain);
|
|
element.set_source(source.into());
|
|
(remain, vec![element])
|
|
}
|
|
Err(_) => {
|
|
let (remaining, (children, _exit_contents)) =
|
|
many_till(element_matcher, exit_matcher)(remaining)?;
|
|
(remaining, children)
|
|
}
|
|
};
|
|
let (remaining, _end) = drawer_end(&parser_context, remaining)?;
|
|
|
|
let (remaining, _trailing_ws) =
|
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
|
let source = get_consumed(input, remaining);
|
|
|
|
Ok((
|
|
remaining,
|
|
Drawer {
|
|
source: source.into(),
|
|
name: get_name(&affiliated_keywords),
|
|
drawer_name: drawer_name.into(),
|
|
children,
|
|
},
|
|
))
|
|
}
|
|
|
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
fn name<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
take_while(|c| WORD_CONSTITUENT_CHARACTERS.contains(c) || "-_".contains(c))(input)
|
|
}
|
|
|
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
fn drawer_end<'b, 'g, 'r, 's>(
|
|
_context: RefContext<'b, 'g, 'r, 's>,
|
|
input: OrgSource<'s>,
|
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
start_of_line(input)?;
|
|
recognize(tuple((
|
|
space0,
|
|
tag_no_case(":end:"),
|
|
space0,
|
|
alt((line_ending, eof)),
|
|
)))(input)
|
|
}
|