Compare commits

...

2 Commits

Author SHA1 Message Date
Tom Alexander
cce9ca87fa
Fix handling of leading blank lines in greater blocks.
Some checks failed
clippy Build clippy has failed
rust-foreign-document-test Build rust-foreign-document-test has failed
rust-build Build rust-build has succeeded
rust-test Build rust-test has failed
2023-12-15 16:55:47 -05:00
Tom Alexander
683c523ece
Implement the new fields for greater block. 2023-12-15 16:15:22 -05:00
2 changed files with 87 additions and 31 deletions

View File

@ -5,22 +5,21 @@ use nom::character::complete::anychar;
use nom::character::complete::line_ending; use nom::character::complete::line_ending;
use nom::character::complete::space0; use nom::character::complete::space0;
use nom::character::complete::space1; use nom::character::complete::space1;
use nom::combinator::consumed;
use nom::combinator::eof; use nom::combinator::eof;
use nom::combinator::not; use nom::combinator::not;
use nom::combinator::opt; use nom::combinator::opt;
use nom::combinator::peek; use nom::combinator::peek;
use nom::combinator::recognize; use nom::combinator::recognize;
use nom::combinator::verify; use nom::combinator::verify;
use nom::multi::many0;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::preceded;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::affiliated_keyword::parse_affiliated_keywords; use super::affiliated_keyword::parse_affiliated_keywords;
use super::org_source::OrgSource; use super::org_source::OrgSource;
use super::paragraph::empty_paragraph;
use super::util::in_section; use super::util::in_section;
use super::util::maybe_consume_trailing_whitespace_if_not_exiting; use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
use crate::context::bind_context;
use crate::context::parser_with_context; use crate::context::parser_with_context;
use crate::context::ContextElement; use crate::context::ContextElement;
use crate::context::ContextMatcher; use crate::context::ContextMatcher;
@ -37,7 +36,6 @@ use crate::parser::util::start_of_line;
use crate::types::CenterBlock; use crate::types::CenterBlock;
use crate::types::Element; use crate::types::Element;
use crate::types::Keyword; use crate::types::Keyword;
use crate::types::Paragraph;
use crate::types::QuoteBlock; use crate::types::QuoteBlock;
use crate::types::SpecialBlock; use crate::types::SpecialBlock;
@ -102,7 +100,7 @@ fn center_block<'b, 'g, 'r, 's, AK>(
where where
AK: IntoIterator<Item = Keyword<'s>>, AK: IntoIterator<Item = Keyword<'s>>,
{ {
let (remaining, (source, children)) = greater_block_body( let (remaining, body) = greater_block_body(
context, context,
input, input,
pre_affiliated_keywords_input, pre_affiliated_keywords_input,
@ -112,12 +110,14 @@ where
Ok(( Ok((
remaining, remaining,
Element::CenterBlock(CenterBlock { Element::CenterBlock(CenterBlock {
source, source: body.source,
affiliated_keywords: parse_affiliated_keywords( affiliated_keywords: parse_affiliated_keywords(
context.get_global_settings(), context.get_global_settings(),
affiliated_keywords, affiliated_keywords,
), ),
children, children: body.children,
contents: body.contents,
post_blank: body.post_blank,
}), }),
)) ))
} }
@ -135,7 +135,7 @@ fn quote_block<'b, 'g, 'r, 's, AK>(
where where
AK: IntoIterator<Item = Keyword<'s>>, AK: IntoIterator<Item = Keyword<'s>>,
{ {
let (remaining, (source, children)) = greater_block_body( let (remaining, body) = greater_block_body(
context, context,
input, input,
pre_affiliated_keywords_input, pre_affiliated_keywords_input,
@ -145,12 +145,14 @@ where
Ok(( Ok((
remaining, remaining,
Element::QuoteBlock(QuoteBlock { Element::QuoteBlock(QuoteBlock {
source, source: body.source,
affiliated_keywords: parse_affiliated_keywords( affiliated_keywords: parse_affiliated_keywords(
context.get_global_settings(), context.get_global_settings(),
affiliated_keywords, affiliated_keywords,
), ),
children, children: body.children,
contents: body.contents,
post_blank: body.post_blank,
}), }),
)) ))
} }
@ -196,7 +198,7 @@ where
AK: IntoIterator<Item = Keyword<'s>>, AK: IntoIterator<Item = Keyword<'s>>,
{ {
let (remaining, parameters) = opt(tuple((space1, parameters)))(input)?; let (remaining, parameters) = opt(tuple((space1, parameters)))(input)?;
let (remaining, (source, children)) = greater_block_body( let (remaining, body) = greater_block_body(
context, context,
remaining, remaining,
pre_affiliated_keywords_input, pre_affiliated_keywords_input,
@ -206,18 +208,28 @@ where
Ok(( Ok((
remaining, remaining,
Element::SpecialBlock(SpecialBlock { Element::SpecialBlock(SpecialBlock {
source, source: body.source,
affiliated_keywords: parse_affiliated_keywords( affiliated_keywords: parse_affiliated_keywords(
context.get_global_settings(), context.get_global_settings(),
affiliated_keywords, affiliated_keywords,
), ),
children, children: body.children,
block_type: name, block_type: name,
parameters: parameters.map(|(_, parameters)| Into::<&str>::into(parameters)), parameters: parameters.map(|(_, parameters)| Into::<&str>::into(parameters)),
contents: body.contents,
post_blank: body.post_blank,
}), }),
)) ))
} }
#[derive(Debug)]
struct GreaterBlockBody<'s> {
source: &'s str,
children: Vec<Element<'s>>,
contents: Option<&'s str>,
post_blank: Option<&'s str>,
}
#[cfg_attr( #[cfg_attr(
feature = "tracing", feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context)) tracing::instrument(ret, level = "debug", skip(context))
@ -228,7 +240,7 @@ fn greater_block_body<'c, 'b, 'g, 'r, 's>(
pre_affiliated_keywords_input: OrgSource<'s>, pre_affiliated_keywords_input: OrgSource<'s>,
name: &'c str, name: &'c str,
context_name: &'c str, context_name: &'c str,
) -> Res<OrgSource<'s>, (&'s str, Vec<Element<'s>>)> { ) -> Res<OrgSource<'s>, GreaterBlockBody<'s>> {
if in_section(context, context_name) { if in_section(context, context_name) {
return Err(nom::Err::Error(CustomError::Static( return Err(nom::Err::Error(CustomError::Static(
"Cannot nest objects of the same element", "Cannot nest objects of the same element",
@ -250,28 +262,43 @@ fn greater_block_body<'c, 'b, 'g, 'r, 's>(
let element_matcher = parser_with_context!(element(true))(&parser_context); let element_matcher = parser_with_context!(element(true))(&parser_context);
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
not(exit_matcher)(remaining)?; not(exit_matcher)(remaining)?;
let (remaining, leading_blank_lines) = opt(consumed(tuple(( let contents_begin = remaining;
blank_line,
many0(preceded(not(exit_matcher), blank_line)), let blank_line_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
))))(remaining)?; class: ExitClass::Alpha,
let leading_blank_lines = exit_matcher: &leading_blank_lines_end,
leading_blank_lines.map(|(source, (first_line, _remaining_lines))| { });
Element::Paragraph(Paragraph::of_text(source.into(), first_line.into())) let blank_line_context = parser_context.with_additional_node(&blank_line_context);
});
let (remaining, leading_blank_lines) =
opt(bind_context!(empty_paragraph, &blank_line_context))(remaining)?;
let (remaining, (mut children, _exit_contents)) = let (remaining, (mut children, _exit_contents)) =
many_till(element_matcher, exit_matcher)(remaining)?; many_till(element_matcher, exit_matcher)(remaining)?;
if let Some(lines) = leading_blank_lines { if let Some(lines) = leading_blank_lines {
children.insert(0, lines); children.insert(0, Element::Paragraph(lines));
} }
let contents = get_consumed(contents_begin, remaining);
let (remaining, _end) = exit_with_name(&parser_context, remaining)?; let (remaining, _end) = exit_with_name(&parser_context, remaining)?;
// Not checking if parent exit matcher is causing exit because the greater_block_end matcher asserts we matched a full greater block // Not checking if parent exit matcher is causing exit because the greater_block_end matcher asserts we matched a full greater block
let (remaining, _trailing_ws) = let (remaining, post_blank) =
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(pre_affiliated_keywords_input, remaining); let source = get_consumed(pre_affiliated_keywords_input, remaining);
Ok((remaining, (Into::<&str>::into(source), children))) Ok((
remaining,
GreaterBlockBody {
source: Into::<&str>::into(source),
children,
contents: if contents.len() > 0 {
Some(Into::<&str>::into(contents))
} else {
None
},
post_blank: post_blank.map(Into::<&str>::into),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
@ -307,3 +334,14 @@ fn _greater_block_end<'b, 'g, 'r, 's, 'c>(
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, source)) Ok((remaining, source))
} }
#[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(_context))
)]
fn leading_blank_lines_end<'b, 'g, 'r, 's, 'c>(
_context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
recognize(not(blank_line))(input)
}

View File

@ -56,6 +56,8 @@ pub struct CenterBlock<'s> {
pub source: &'s str, pub source: &'s str,
pub affiliated_keywords: AffiliatedKeywords<'s>, pub affiliated_keywords: AffiliatedKeywords<'s>,
pub children: Vec<Element<'s>>, pub children: Vec<Element<'s>>,
pub contents: Option<&'s str>,
pub post_blank: Option<&'s str>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -63,6 +65,8 @@ pub struct QuoteBlock<'s> {
pub source: &'s str, pub source: &'s str,
pub affiliated_keywords: AffiliatedKeywords<'s>, pub affiliated_keywords: AffiliatedKeywords<'s>,
pub children: Vec<Element<'s>>, pub children: Vec<Element<'s>>,
pub contents: Option<&'s str>,
pub post_blank: Option<&'s str>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -72,6 +76,8 @@ pub struct SpecialBlock<'s> {
pub block_type: &'s str, pub block_type: &'s str,
pub parameters: Option<&'s str>, pub parameters: Option<&'s str>,
pub children: Vec<Element<'s>>, pub children: Vec<Element<'s>>,
pub contents: Option<&'s str>,
pub post_blank: Option<&'s str>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -180,11 +186,15 @@ impl<'s> StandardProperties<'s> for CenterBlock<'s> {
} }
fn get_contents<'b>(&'b self) -> Option<&'s str> { fn get_contents<'b>(&'b self) -> Option<&'s str> {
todo!() self.contents
} }
fn get_post_blank(&self) -> PostBlank { fn get_post_blank(&self) -> PostBlank {
todo!() self.post_blank
.map(|text| text.lines().count())
.unwrap_or(0)
.try_into()
.expect("Too much post-blank to fit into a PostBlank.")
} }
} }
@ -194,11 +204,15 @@ impl<'s> StandardProperties<'s> for QuoteBlock<'s> {
} }
fn get_contents<'b>(&'b self) -> Option<&'s str> { fn get_contents<'b>(&'b self) -> Option<&'s str> {
todo!() self.contents
} }
fn get_post_blank(&self) -> PostBlank { fn get_post_blank(&self) -> PostBlank {
todo!() self.post_blank
.map(|text| text.lines().count())
.unwrap_or(0)
.try_into()
.expect("Too much post-blank to fit into a PostBlank.")
} }
} }
@ -208,11 +222,15 @@ impl<'s> StandardProperties<'s> for SpecialBlock<'s> {
} }
fn get_contents<'b>(&'b self) -> Option<&'s str> { fn get_contents<'b>(&'b self) -> Option<&'s str> {
todo!() self.contents
} }
fn get_post_blank(&self) -> PostBlank { fn get_post_blank(&self) -> PostBlank {
todo!() self.post_blank
.map(|text| text.lines().count())
.unwrap_or(0)
.try_into()
.expect("Too much post-blank to fit into a PostBlank.")
} }
} }