diff --git a/src/parser/document.rs b/src/parser/document.rs index 44aca17f..027ba70b 100644 --- a/src/parser/document.rs +++ b/src/parser/document.rs @@ -1,42 +1,28 @@ use nom::combinator::all_consuming; use nom::combinator::opt; -use nom::combinator::recognize; -use nom::combinator::verify; use nom::multi::many0; -use nom::multi::many_till; -use nom::sequence::tuple; -use super::headline::detect_headline; use super::headline::heading; use super::in_buffer_settings::apply_in_buffer_settings; use super::in_buffer_settings::scan_for_in_buffer_settings; use super::org_source::OrgSource; +use super::section::zeroth_section; use super::token::AllTokensIterator; use super::token::Token; -use super::util::exit_matcher_parser; use super::util::get_consumed; use crate::context::parser_with_context; use crate::context::Context; use crate::context::ContextElement; -use crate::context::ExitClass; -use crate::context::ExitMatcherNode; use crate::context::GlobalSettings; use crate::context::List; use crate::context::RefContext; use crate::error::CustomError; use crate::error::MyError; use crate::error::Res; -use crate::parser::comment::comment; -use crate::parser::element_parser::element; use crate::parser::org_source::convert_error; -use crate::parser::planning::planning; -use crate::parser::property_drawer::property_drawer; use crate::parser::util::blank_line; -use crate::parser::util::maybe_consume_trailing_whitespace_if_not_exiting; use crate::types::Document; -use crate::types::Element; use crate::types::Object; -use crate::types::Section; /// Parse a full org-mode document. /// @@ -170,127 +156,6 @@ fn _document<'b, 'g, 'r, 's>( )) } -#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn zeroth_section<'b, 'g, 'r, 's>( - context: RefContext<'b, 'g, 'r, 's>, - input: OrgSource<'s>, -) -> Res, Section<'s>> { - // TODO: The zeroth section is specialized so it probably needs its own parser - let contexts = [ - ContextElement::ConsumeTrailingWhitespace(true), - ContextElement::Context("section"), - ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Document, - exit_matcher: §ion_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 without_consuming_whitespace_context = ContextElement::ConsumeTrailingWhitespace(false); - let without_consuming_whitespace_context = - parser_context.with_additional_node(&without_consuming_whitespace_context); - - let element_matcher = parser_with_context!(element(true))(&parser_context); - let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); - - let (remaining, comment_and_property_drawer_element) = opt(tuple(( - opt(parser_with_context!(comment)( - &without_consuming_whitespace_context, - )), - parser_with_context!(property_drawer)(context), - many0(blank_line), - )))(input)?; - - let (remaining, (mut children, _exit_contents)) = verify( - many_till(element_matcher, exit_matcher), - |(children, _exit_contents)| { - !children.is_empty() || comment_and_property_drawer_element.is_some() - }, - )(remaining)?; - - comment_and_property_drawer_element.map(|(comment, property_drawer, _ws)| { - children.insert(0, Element::PropertyDrawer(property_drawer)); - comment - .map(Element::Comment) - .map(|ele| children.insert(0, ele)); - }); - - let (remaining, _trailing_ws) = - maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; - - let source = get_consumed(input, remaining); - Ok(( - remaining, - Section { - source: source.into(), - children, - }, - )) -} - -#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -pub fn section<'b, 'g, 'r, 's>( - context: RefContext<'b, 'g, 'r, 's>, - mut input: OrgSource<'s>, -) -> Res, Section<'s>> { - // TODO: The zeroth section is specialized so it probably needs its own parser - let contexts = [ - ContextElement::ConsumeTrailingWhitespace(true), - ContextElement::Context("section"), - ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Document, - exit_matcher: §ion_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 (mut remaining, (planning_element, property_drawer_element)) = tuple(( - opt(parser_with_context!(planning)(&parser_context)), - opt(parser_with_context!(property_drawer)(&parser_context)), - ))(input)?; - if planning_element.is_none() && property_drawer_element.is_none() { - let (remain, _ws) = many0(blank_line)(remaining)?; - remaining = remain; - input = remain; - } - let (remaining, (mut children, _exit_contents)) = verify( - many_till(element_matcher, exit_matcher), - |(children, _exit_contents)| { - !children.is_empty() || property_drawer_element.is_some() || planning_element.is_some() - }, - )(remaining)?; - property_drawer_element - .map(Element::PropertyDrawer) - .map(|ele| children.insert(0, ele)); - planning_element - .map(Element::Planning) - .map(|ele| children.insert(0, ele)); - - let (remaining, _trailing_ws) = - maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; - - let source = get_consumed(input, remaining); - Ok(( - remaining, - Section { - source: source.into(), - children, - }, - )) -} - -#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn section_end<'b, 'g, 'r, 's>( - _context: RefContext<'b, 'g, 'r, 's>, - input: OrgSource<'s>, -) -> Res, OrgSource<'s>> { - recognize(detect_headline)(input) -} - impl<'s> Document<'s> { pub fn iter_tokens<'r>(&'r self) -> impl Iterator> { AllTokensIterator::new(Token::Document(self)) diff --git a/src/parser/headline.rs b/src/parser/headline.rs index 0146badc..c0b67025 100644 --- a/src/parser/headline.rs +++ b/src/parser/headline.rs @@ -16,8 +16,8 @@ use nom::multi::many1_count; use nom::multi::separated_list1; use nom::sequence::tuple; -use super::document::section; use super::org_source::OrgSource; +use super::section::section; use super::util::get_consumed; use super::util::start_of_line; use crate::context::parser_with_context; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index f402d2aa..30b685a0 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -36,6 +36,7 @@ mod planning; mod property_drawer; mod radio_link; mod regular_link; +mod section; pub mod sexp; mod statistics_cookie; mod subscript_and_superscript; diff --git a/src/parser/section.rs b/src/parser/section.rs new file mode 100644 index 00000000..33b16854 --- /dev/null +++ b/src/parser/section.rs @@ -0,0 +1,146 @@ +use nom::combinator::opt; +use nom::combinator::recognize; +use nom::combinator::verify; +use nom::multi::many0; +use nom::multi::many_till; +use nom::sequence::tuple; + +use super::headline::detect_headline; +use super::org_source::OrgSource; +use super::util::exit_matcher_parser; +use super::util::get_consumed; +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::Res; +use crate::parser::comment::comment; +use crate::parser::element_parser::element; +use crate::parser::planning::planning; +use crate::parser::property_drawer::property_drawer; +use crate::parser::util::blank_line; +use crate::parser::util::maybe_consume_trailing_whitespace_if_not_exiting; +use crate::types::Element; +use crate::types::Section; + +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +pub fn zeroth_section<'b, 'g, 'r, 's>( + context: RefContext<'b, 'g, 'r, 's>, + input: OrgSource<'s>, +) -> Res, Section<'s>> { + // TODO: The zeroth section is specialized so it probably needs its own parser + let contexts = [ + ContextElement::ConsumeTrailingWhitespace(true), + ContextElement::Context("section"), + ContextElement::ExitMatcherNode(ExitMatcherNode { + class: ExitClass::Document, + exit_matcher: §ion_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 without_consuming_whitespace_context = ContextElement::ConsumeTrailingWhitespace(false); + let without_consuming_whitespace_context = + parser_context.with_additional_node(&without_consuming_whitespace_context); + + let element_matcher = parser_with_context!(element(true))(&parser_context); + let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); + + let (remaining, comment_and_property_drawer_element) = opt(tuple(( + opt(parser_with_context!(comment)( + &without_consuming_whitespace_context, + )), + parser_with_context!(property_drawer)(context), + many0(blank_line), + )))(input)?; + + let (remaining, (mut children, _exit_contents)) = verify( + many_till(element_matcher, exit_matcher), + |(children, _exit_contents)| { + !children.is_empty() || comment_and_property_drawer_element.is_some() + }, + )(remaining)?; + + comment_and_property_drawer_element.map(|(comment, property_drawer, _ws)| { + children.insert(0, Element::PropertyDrawer(property_drawer)); + comment + .map(Element::Comment) + .map(|ele| children.insert(0, ele)); + }); + + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; + + let source = get_consumed(input, remaining); + Ok(( + remaining, + Section { + source: source.into(), + children, + }, + )) +} + +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +pub fn section<'b, 'g, 'r, 's>( + context: RefContext<'b, 'g, 'r, 's>, + mut input: OrgSource<'s>, +) -> Res, Section<'s>> { + // TODO: The zeroth section is specialized so it probably needs its own parser + let contexts = [ + ContextElement::ConsumeTrailingWhitespace(true), + ContextElement::Context("section"), + ContextElement::ExitMatcherNode(ExitMatcherNode { + class: ExitClass::Document, + exit_matcher: §ion_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 (mut remaining, (planning_element, property_drawer_element)) = tuple(( + opt(parser_with_context!(planning)(&parser_context)), + opt(parser_with_context!(property_drawer)(&parser_context)), + ))(input)?; + if planning_element.is_none() && property_drawer_element.is_none() { + let (remain, _ws) = many0(blank_line)(remaining)?; + remaining = remain; + input = remain; + } + let (remaining, (mut children, _exit_contents)) = verify( + many_till(element_matcher, exit_matcher), + |(children, _exit_contents)| { + !children.is_empty() || property_drawer_element.is_some() || planning_element.is_some() + }, + )(remaining)?; + property_drawer_element + .map(Element::PropertyDrawer) + .map(|ele| children.insert(0, ele)); + planning_element + .map(Element::Planning) + .map(|ele| children.insert(0, ele)); + + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; + + let source = get_consumed(input, remaining); + Ok(( + remaining, + Section { + source: source.into(), + children, + }, + )) +} + +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +fn section_end<'b, 'g, 'r, 's>( + _context: RefContext<'b, 'g, 'r, 's>, + input: OrgSource<'s>, +) -> Res, OrgSource<'s>> { + recognize(detect_headline)(input) +}