From f79606047eec3975a56d27a6519455718c32b650 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 5 Oct 2023 20:13:10 -0400 Subject: [PATCH 1/2] Compare value. --- src/compare/diff.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 2cf311e..0cbd284 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -2484,8 +2484,6 @@ fn compare_latex_environment<'b, 's>( let mut this_status = DiffStatus::Good; let mut message = None; - // TODO: Compare :value - // TODO: Compare :caption // Compare name let name = get_property_quoted_string(emacs, ":name")?; @@ -2497,6 +2495,16 @@ fn compare_latex_environment<'b, 's>( )); } + // Compare value + let value = get_property_quoted_string(emacs, ":value")?; + if value.as_ref().map(String::as_str) != Some(rust.source) { + this_status = DiffStatus::Bad; + message = Some(format!( + "Value mismatch (emacs != rust) {:?} != {:?}", + value, rust.source + )); + } + Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), From 758e224e6d6cd83fd93e665bdc44f463ea28ad12 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 6 Oct 2023 11:45:15 -0400 Subject: [PATCH 2/2] Move consuming trailing element whitespace inside the parsers. This ensures the parsers can take into account the affiliated keywords when setting their source without needing the SetSource trait. --- src/compare/diff.rs | 4 +- src/parser/babel_call.rs | 13 ++++-- src/parser/clock.rs | 3 ++ src/parser/comment.rs | 3 ++ src/parser/diary_sexp.rs | 11 ++++-- src/parser/drawer.rs | 9 +++-- src/parser/dynamic_block.rs | 9 +++-- src/parser/element_parser.rs | 17 +++----- src/parser/fixed_width_area.rs | 7 +++- src/parser/footnote_definition.rs | 9 +++-- src/parser/greater_block.rs | 66 +++++++++++++++++++++++-------- src/parser/horizontal_rule.rs | 17 +++++--- src/parser/keyword.rs | 25 ++++++++++-- src/parser/latex_environment.rs | 13 ++++-- src/parser/lesser_block.rs | 31 ++++++++++----- src/parser/paragraph.rs | 9 +++-- src/parser/plain_list.rs | 6 ++- src/parser/table.rs | 11 ++++-- src/types/lesser_element.rs | 1 + 19 files changed, 185 insertions(+), 79 deletions(-) diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 0cbd284..66de6b3 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -2497,11 +2497,11 @@ fn compare_latex_environment<'b, 's>( // Compare value let value = get_property_quoted_string(emacs, ":value")?; - if value.as_ref().map(String::as_str) != Some(rust.source) { + if value.as_ref().map(String::as_str) != Some(rust.value) { this_status = DiffStatus::Bad; message = Some(format!( "Value mismatch (emacs != rust) {:?} != {:?}", - value, rust.source + value, rust.value )); } diff --git a/src/parser/babel_call.rs b/src/parser/babel_call.rs index 2919422..6903444 100644 --- a/src/parser/babel_call.rs +++ b/src/parser/babel_call.rs @@ -17,6 +17,7 @@ use nom::InputTake; use super::keyword::affiliated_keyword; use super::org_source::BracketDepth; use super::util::get_name; +use super::util::maybe_consume_trailing_whitespace_if_not_exiting; use super::util::start_of_line; use super::OrgSource; use crate::context::Matcher; @@ -30,15 +31,17 @@ use crate::types::BabelCall; #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub(crate) fn babel_call<'b, 'g, 'r, 's>( - _context: RefContext<'b, 'g, 'r, 's>, + context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, BabelCall<'s>> { - let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; + let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; - start_of_line(input)?; - let (remaining, _) = tuple((space0, tag("#+"), tag_no_case("call"), tag(":")))(input)?; + start_of_line(remaining)?; + let (remaining, _) = tuple((space0, tag("#+"), tag_no_case("call"), tag(":")))(remaining)?; if let Ok((remaining, (_, line_break))) = tuple((space0, org_line_ending))(remaining) { + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; let source = get_consumed(input, remaining); return Ok(( remaining, @@ -59,6 +62,8 @@ pub(crate) fn babel_call<'b, 'g, 'r, 's>( consumed(babel_call_value)(remaining)?; let (remaining, _ws) = tuple((space0, org_line_ending))(remaining)?; + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; let source = get_consumed(input, remaining); Ok(( diff --git a/src/parser/clock.rs b/src/parser/clock.rs index 5b4d8cd..9941510 100644 --- a/src/parser/clock.rs +++ b/src/parser/clock.rs @@ -13,6 +13,7 @@ use super::org_source::OrgSource; use super::timestamp::inactive_date_range_timestamp; use super::timestamp::inactive_time_range_timestamp; use super::timestamp::inactive_timestamp; +use super::util::maybe_consume_trailing_whitespace_if_not_exiting; use super::util::org_line_ending; use crate::context::parser_with_context; use crate::context::RefContext; @@ -36,6 +37,8 @@ pub(crate) fn clock<'b, 'g, 'r, 's>( let (remaining, (timestamp, duration)) = clock_timestamp(context, remaining)?; let (remaining, _) = tuple((space0, org_line_ending))(remaining)?; + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; let source = get_consumed(input, remaining); Ok(( remaining, diff --git a/src/parser/comment.rs b/src/parser/comment.rs index a6ded5d..09d74e5 100644 --- a/src/parser/comment.rs +++ b/src/parser/comment.rs @@ -13,6 +13,7 @@ use nom::sequence::tuple; use super::org_source::OrgSource; use super::util::get_consumed; +use super::util::maybe_consume_trailing_whitespace_if_not_exiting; use super::util::org_line_ending; use crate::context::parser_with_context; use crate::context::ContextElement; @@ -43,6 +44,8 @@ pub(crate) fn comment<'b, 'g, 'r, 's>( let (remaining, mut remaining_lines) = many0(preceded(not(exit_matcher), comment_line_matcher))(remaining)?; + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; let source = get_consumed(input, remaining); let mut value = Vec::with_capacity(remaining_lines.len() + 1); let last_line = remaining_lines.pop(); diff --git a/src/parser/diary_sexp.rs b/src/parser/diary_sexp.rs index 235fcab..7e3d173 100644 --- a/src/parser/diary_sexp.rs +++ b/src/parser/diary_sexp.rs @@ -7,6 +7,7 @@ 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 super::util::org_line_ending; use crate::context::RefContext; use crate::error::Res; @@ -16,14 +17,16 @@ use crate::types::DiarySexp; #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub(crate) fn diary_sexp<'b, 'g, 'r, 's>( - _context: RefContext<'b, 'g, 'r, 's>, + context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, DiarySexp<'s>> { - let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; - start_of_line(input)?; - let (remaining, value) = recognize(tuple((tag("%%("), is_not("\r\n"))))(input)?; + let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; + start_of_line(remaining)?; + let (remaining, value) = recognize(tuple((tag("%%("), is_not("\r\n"))))(remaining)?; let (remaining, _eol) = org_line_ending(remaining)?; + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; let source = get_consumed(input, remaining); Ok(( remaining, diff --git a/src/parser/drawer.rs b/src/parser/drawer.rs index c78f80d..2cd3256 100644 --- a/src/parser/drawer.rs +++ b/src/parser/drawer.rs @@ -14,6 +14,7 @@ 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; @@ -44,9 +45,9 @@ pub(crate) fn drawer<'b, 'g, 'r, 's>( "Cannot nest objects of the same element".into(), )))); } - let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; - start_of_line(input)?; - let (remaining, _leading_whitespace) = space0(input)?; + 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, @@ -88,6 +89,8 @@ pub(crate) fn drawer<'b, 'g, 'r, 's>( }; 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(( diff --git a/src/parser/dynamic_block.rs b/src/parser/dynamic_block.rs index b9a9d5e..39f43c8 100644 --- a/src/parser/dynamic_block.rs +++ b/src/parser/dynamic_block.rs @@ -20,6 +20,7 @@ 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; @@ -49,10 +50,10 @@ pub(crate) fn dynamic_block<'b, 'g, 'r, 's>( "Cannot nest objects of the same element".into(), )))); } - let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; + let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; - start_of_line(input)?; - let (remaining, _leading_whitespace) = space0(input)?; + start_of_line(remaining)?; + let (remaining, _leading_whitespace) = space0(remaining)?; let (remaining, (_, name, parameters, _, _)) = tuple(( recognize(tuple((tag_no_case("#+begin:"), space1))), name, @@ -96,6 +97,8 @@ pub(crate) fn dynamic_block<'b, 'g, 'r, 's>( let (remaining, _end) = dynamic_block_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, diff --git a/src/parser/element_parser.rs b/src/parser/element_parser.rs index b416831..ead31e6 100644 --- a/src/parser/element_parser.rs +++ b/src/parser/element_parser.rs @@ -21,6 +21,7 @@ use super::footnote_definition::footnote_definition; use super::greater_block::greater_block; use super::horizontal_rule::horizontal_rule; use super::keyword::affiliated_keyword; +use super::keyword::affiliated_keyword_as_regular_keyword; use super::keyword::keyword; use super::latex_environment::latex_environment; use super::lesser_block::comment_block; @@ -33,8 +34,6 @@ use super::paragraph::paragraph; use super::plain_list::detect_plain_list; use super::plain_list::plain_list; use super::table::detect_table; -use super::util::get_consumed; -use super::util::maybe_consume_trailing_whitespace_if_not_exiting; use crate::context::parser_with_context; use crate::context::RefContext; use crate::error::CustomError; @@ -42,7 +41,6 @@ use crate::error::MyError; use crate::error::Res; use crate::parser::table::org_mode_table; use crate::types::Element; -use crate::types::SetSource; pub(crate) const fn element( can_be_paragraph: bool, @@ -137,7 +135,10 @@ fn _element<'b, 'g, 'r, 's>( #[cfg(feature = "tracing")] let _enter = span.enter(); - let (remain, kw) = opt(map(affiliated_keyword, Element::Keyword))(remaining)?; + let (remain, kw) = opt(map( + parser_with_context!(affiliated_keyword_as_regular_keyword)(context), + Element::Keyword, + ))(remaining)?; if kw.is_some() { maybe_element = kw; remaining = remain; @@ -164,13 +165,7 @@ fn _element<'b, 'g, 'r, 's>( "No element.", )))); } - let mut element = maybe_element.expect("The above if-statement ensures this is Some()."); - - let (remaining, _trailing_ws) = - maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; - - let source = get_consumed(input, remaining); - element.set_source(source.into()); + let element = maybe_element.expect("The above if-statement ensures this is Some()."); Ok((remaining, element)) } diff --git a/src/parser/fixed_width_area.rs b/src/parser/fixed_width_area.rs index e51264b..466eb59 100644 --- a/src/parser/fixed_width_area.rs +++ b/src/parser/fixed_width_area.rs @@ -12,6 +12,7 @@ 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 super::util::org_line_ending; use crate::context::parser_with_context; use crate::context::RefContext; @@ -26,13 +27,15 @@ pub(crate) fn fixed_width_area<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, FixedWidthArea<'s>> { - let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; + let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; let fixed_width_area_line_matcher = parser_with_context!(fixed_width_area_line)(context); let exit_matcher = parser_with_context!(exit_matcher_parser)(context); - let (remaining, first_line) = fixed_width_area_line_matcher(input)?; + let (remaining, first_line) = fixed_width_area_line_matcher(remaining)?; let (remaining, mut remaining_lines) = many0(preceded(not(exit_matcher), fixed_width_area_line_matcher))(remaining)?; + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; let source = get_consumed(input, remaining); let mut value = Vec::with_capacity(remaining_lines.len() + 1); let last_line = remaining_lines.pop(); diff --git a/src/parser/footnote_definition.rs b/src/parser/footnote_definition.rs index dee1b9d..4a182e3 100644 --- a/src/parser/footnote_definition.rs +++ b/src/parser/footnote_definition.rs @@ -15,6 +15,7 @@ use super::keyword::affiliated_keyword; use super::org_source::OrgSource; use super::util::get_name; use super::util::include_input; +use super::util::maybe_consume_trailing_whitespace_if_not_exiting; use super::util::WORD_CONSTITUENT_CHARACTERS; use crate::context::parser_with_context; use crate::context::ContextElement; @@ -43,8 +44,8 @@ pub(crate) fn footnote_definition<'b, 'g, 'r, 's>( "Cannot nest objects of the same element".into(), )))); } - let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; - start_of_line(input)?; + let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; + start_of_line(remaining)?; // Cannot be indented. let (remaining, (_, lbl, _, _, _)) = tuple(( tag_no_case("[fn:"), @@ -54,7 +55,7 @@ pub(crate) fn footnote_definition<'b, 'g, 'r, 's>( opt(verify(many0(blank_line), |lines: &Vec>| { lines.len() <= 2 })), - ))(input)?; + ))(remaining)?; let contexts = [ ContextElement::ConsumeTrailingWhitespace(true), ContextElement::Context("footnote definition"), @@ -83,6 +84,8 @@ pub(crate) fn footnote_definition<'b, 'g, 'r, 's>( } } + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; let source = get_consumed(input, remaining); Ok(( remaining, diff --git a/src/parser/greater_block.rs b/src/parser/greater_block.rs index 338b26f..3dd09d1 100644 --- a/src/parser/greater_block.rs +++ b/src/parser/greater_block.rs @@ -21,6 +21,7 @@ use super::keyword::affiliated_keyword; use super::org_source::OrgSource; use super::util::get_name; use super::util::in_section; +use super::util::maybe_consume_trailing_whitespace_if_not_exiting; use crate::context::parser_with_context; use crate::context::ContextElement; use crate::context::ContextMatcher; @@ -48,6 +49,7 @@ pub(crate) fn greater_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, Element<'s>> { + let pre_affiliated_keywords_input = input; let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; start_of_line(input)?; let (remaining, _leading_whitespace) = space0(input)?; @@ -62,9 +64,24 @@ pub(crate) fn greater_block<'b, 'g, 'r, 's>( ))(remaining)?; let name = Into::<&str>::into(name); let (remaining, element) = match name.to_lowercase().as_str() { - "center" => center_block(context, remaining, input, &affiliated_keywords)?, - "quote" => quote_block(context, remaining, input, &affiliated_keywords)?, - _ => special_block(name)(context, remaining, input, &affiliated_keywords)?, + "center" => center_block( + context, + remaining, + pre_affiliated_keywords_input, + &affiliated_keywords, + )?, + "quote" => quote_block( + context, + remaining, + pre_affiliated_keywords_input, + &affiliated_keywords, + )?, + _ => special_block(name)( + context, + remaining, + pre_affiliated_keywords_input, + &affiliated_keywords, + )?, }; Ok((remaining, element)) } @@ -73,11 +90,16 @@ pub(crate) fn greater_block<'b, 'g, 'r, 's>( fn center_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, - original_input: OrgSource<'s>, + pre_affiliated_keywords_input: OrgSource<'s>, affiliated_keywords: &Vec>, ) -> Res, Element<'s>> { - let (remaining, (source, children)) = - greater_block_body(context, input, original_input, "center", "center block")?; + let (remaining, (source, children)) = greater_block_body( + context, + input, + pre_affiliated_keywords_input, + "center", + "center block", + )?; Ok(( remaining, Element::CenterBlock(CenterBlock { @@ -92,11 +114,16 @@ fn center_block<'b, 'g, 'r, 's>( fn quote_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, - original_input: OrgSource<'s>, + pre_affiliated_keywords_input: OrgSource<'s>, affiliated_keywords: &Vec>, ) -> Res, Element<'s>> { - let (remaining, (source, children)) = - greater_block_body(context, input, original_input, "quote", "quote block")?; + let (remaining, (source, children)) = greater_block_body( + context, + input, + pre_affiliated_keywords_input, + "quote", + "quote block", + )?; Ok(( remaining, Element::QuoteBlock(QuoteBlock { @@ -117,11 +144,11 @@ fn special_block<'s>( ) -> Res, Element<'s>> + 's { let context_name = format!("special block {}", name); - move |context, input, original_input, affiliated_keywords| { + move |context, input, pre_affiliated_keywords_input, affiliated_keywords| { _special_block( context, input, - original_input, + pre_affiliated_keywords_input, name, context_name.as_str(), affiliated_keywords, @@ -133,14 +160,19 @@ fn special_block<'s>( fn _special_block<'c, 'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, - original_input: OrgSource<'s>, + pre_affiliated_keywords_input: OrgSource<'s>, name: &'s str, context_name: &'c str, affiliated_keywords: &Vec>, ) -> Res, Element<'s>> { let (remaining, parameters) = opt(tuple((space1, parameters)))(input)?; - let (remaining, (source, children)) = - greater_block_body(context, remaining, original_input, name, context_name)?; + let (remaining, (source, children)) = greater_block_body( + context, + remaining, + pre_affiliated_keywords_input, + name, + context_name, + )?; Ok(( remaining, Element::SpecialBlock(SpecialBlock { @@ -157,7 +189,7 @@ fn _special_block<'c, 'b, 'g, 'r, 's>( fn greater_block_body<'c, 'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, - original_input: OrgSource<'s>, + pre_affiliated_keywords_input: OrgSource<'s>, name: &'c str, context_name: &'c str, ) -> Res, (&'s str, Vec>)> { @@ -202,7 +234,9 @@ fn greater_block_body<'c, 'b, 'g, 'r, 's>( // Not checking if parent exit matcher is causing exit because the greater_block_end matcher asserts we matched a full greater block - let source = get_consumed(original_input, remaining); + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; + let source = get_consumed(pre_affiliated_keywords_input, remaining); Ok((remaining, (Into::<&str>::into(source), children))) } diff --git a/src/parser/horizontal_rule.rs b/src/parser/horizontal_rule.rs index 7dca25c..ece5205 100644 --- a/src/parser/horizontal_rule.rs +++ b/src/parser/horizontal_rule.rs @@ -11,7 +11,9 @@ use nom::sequence::tuple; use super::keyword::affiliated_keyword; use super::org_source::OrgSource; +use super::util::get_consumed; use super::util::get_name; +use super::util::maybe_consume_trailing_whitespace_if_not_exiting; use crate::context::RefContext; use crate::error::Res; use crate::parser::util::start_of_line; @@ -19,21 +21,24 @@ use crate::types::HorizontalRule; #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub(crate) fn horizontal_rule<'b, 'g, 'r, 's>( - _context: RefContext<'b, 'g, 'r, 's>, + context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, HorizontalRule<'s>> { - let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; - start_of_line(input)?; - let (remaining, rule) = recognize(tuple(( + let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; + start_of_line(remaining)?; + let (remaining, _rule) = recognize(tuple(( space0, verify(many1_count(tag("-")), |dashes| *dashes >= 5), space0, alt((line_ending, eof)), - )))(input)?; + )))(remaining)?; + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; + let source = get_consumed(input, remaining); Ok(( remaining, HorizontalRule { - source: rule.into(), + source: source.into(), name: get_name(&affiliated_keywords), }, )) diff --git a/src/parser/keyword.rs b/src/parser/keyword.rs index 19aea56..7c4dfc5 100644 --- a/src/parser/keyword.rs +++ b/src/parser/keyword.rs @@ -19,7 +19,9 @@ use nom::sequence::tuple; use super::org_source::BracketDepth; use super::org_source::OrgSource; +use super::util::get_consumed; use super::util::get_name; +use super::util::maybe_consume_trailing_whitespace_if_not_exiting; use crate::context::Matcher; use crate::context::RefContext; use crate::error::CustomError; @@ -89,12 +91,29 @@ fn _filtered_keyword<'s, F: Matcher>( #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub(crate) fn keyword<'b, 'g, 'r, 's>( - _context: RefContext<'b, 'g, 'r, 's>, + context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, Keyword<'s>> { - let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; - let (remaining, mut kw) = filtered_keyword(regular_keyword_key)(input)?; + let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; + let (remaining, mut kw) = filtered_keyword(regular_keyword_key)(remaining)?; + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; + let source = get_consumed(input, remaining); kw.name = get_name(&affiliated_keywords); + kw.source = Into::<&str>::into(source); + Ok((remaining, kw)) +} + +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +pub(crate) fn affiliated_keyword_as_regular_keyword<'b, 'g, 'r, 's>( + context: RefContext<'b, 'g, 'r, 's>, + input: OrgSource<'s>, +) -> Res, Keyword<'s>> { + let (remaining, mut kw) = affiliated_keyword(input)?; + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; + let source = get_consumed(input, remaining); + kw.source = Into::<&str>::into(source); Ok((remaining, kw)) } diff --git a/src/parser/latex_environment.rs b/src/parser/latex_environment.rs index b175d70..3167164 100644 --- a/src/parser/latex_environment.rs +++ b/src/parser/latex_environment.rs @@ -16,6 +16,7 @@ use super::keyword::affiliated_keyword; use super::org_source::OrgSource; use super::util::get_consumed; 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::ContextMatcher; @@ -32,9 +33,10 @@ pub(crate) fn latex_environment<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, LatexEnvironment<'s>> { - let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; - start_of_line(input)?; - let (remaining, _leading_whitespace) = space0(input)?; + let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; + let value_start = remaining; + start_of_line(remaining)?; + let (remaining, _leading_whitespace) = space0(remaining)?; let (remaining, (_opening, name, _open_close_brace, _ws, _line_ending)) = tuple(( tag_no_case(r#"\begin{"#), name, @@ -52,13 +54,18 @@ pub(crate) fn latex_environment<'b, 'g, 'r, 's>( let (remaining, _contents) = contents(&latex_environment_end_specialized)(context, remaining)?; let (remaining, _end) = latex_environment_end_specialized(&parser_context, remaining)?; + let value_end = remaining; + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; let source = get_consumed(input, remaining); + let value = get_consumed(value_start, value_end); Ok(( remaining, LatexEnvironment { source: source.into(), name: get_name(&affiliated_keywords), + value: value.into(), }, )) } diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 5e6c9d0..e810c73 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -20,6 +20,7 @@ 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::ContextMatcher; @@ -52,8 +53,8 @@ pub(crate) fn verse_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, VerseBlock<'s>> { - let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; - let (remaining, _) = lesser_block_begin("verse")(context, input)?; + let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; + let (remaining, _) = lesser_block_begin("verse")(context, remaining)?; let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?; let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?; let lesser_block_end_specialized = lesser_block_end("verse"); @@ -91,6 +92,8 @@ pub(crate) fn verse_block<'b, 'g, 'r, 's>( }; let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?; + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; let source = get_consumed(input, remaining); Ok(( remaining, @@ -108,8 +111,8 @@ pub(crate) fn comment_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, CommentBlock<'s>> { - let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; - let (remaining, _) = lesser_block_begin("comment")(context, input)?; + let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; + let (remaining, _) = lesser_block_begin("comment")(context, remaining)?; let (remaining, _parameters) = opt(tuple((space1, data)))(remaining)?; let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?; let lesser_block_end_specialized = lesser_block_end("comment"); @@ -128,6 +131,8 @@ pub(crate) fn comment_block<'b, 'g, 'r, 's>( let (remaining, contents) = parser_with_context!(text_until_exit)(&parser_context)(remaining)?; let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?; + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; let source = get_consumed(input, remaining); Ok(( remaining, @@ -144,8 +149,8 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, ExampleBlock<'s>> { - let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; - let (remaining, _) = lesser_block_begin("example")(context, input)?; + let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; + let (remaining, _) = lesser_block_begin("example")(context, remaining)?; let (remaining, parameters) = opt(tuple((space1, example_switches)))(remaining)?; let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?; let lesser_block_end_specialized = lesser_block_end("example"); @@ -165,6 +170,8 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>( let (remaining, contents) = content(&parser_context, remaining)?; let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?; + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; let source = get_consumed(input, remaining); let (switches, number_lines, preserve_indent, retain_labels, use_labels, label_format) = { if let Some(parameters) = parameters { @@ -205,8 +212,8 @@ pub(crate) fn export_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, ExportBlock<'s>> { - let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; - let (remaining, _) = lesser_block_begin("export")(context, input)?; + let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; + let (remaining, _) = lesser_block_begin("export")(context, remaining)?; // https://orgmode.org/worg/org-syntax.html#Blocks claims that export blocks must have a single word for data but testing shows no data and multi-word data still parses as an export block. let (remaining, export_type) = opt(map( tuple((space1, switch_word, peek(tuple((space0, line_ending))))), @@ -231,6 +238,8 @@ pub(crate) fn export_block<'b, 'g, 'r, 's>( let (remaining, contents) = content(&parser_context, remaining)?; let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?; + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; let source = get_consumed(input, remaining); Ok(( remaining, @@ -249,8 +258,8 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, SrcBlock<'s>> { - let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; - let (remaining, _) = lesser_block_begin("src")(context, input)?; + let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; + let (remaining, _) = lesser_block_begin("src")(context, remaining)?; // https://orgmode.org/worg/org-syntax.html#Blocks claims that data is mandatory and must follow the LANGUAGE SWITCHES ARGUMENTS pattern but testing has shown that no data and incorrect data here will still parse to a src block. let (remaining, language) = opt(map(tuple((space1, switch_word)), |(_, language)| language))(remaining)?; @@ -274,6 +283,8 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( let (remaining, contents) = content(&parser_context, remaining)?; let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?; + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; let source = get_consumed(input, remaining); let (switches, number_lines, preserve_indent, retain_labels, use_labels, label_format) = { if let Some(switches) = switches { diff --git a/src/parser/paragraph.rs b/src/parser/paragraph.rs index 2bce4b5..746e23f 100644 --- a/src/parser/paragraph.rs +++ b/src/parser/paragraph.rs @@ -14,6 +14,7 @@ use super::util::blank_line; use super::util::get_consumed; use super::util::get_has_affiliated_keyword; 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; @@ -33,10 +34,10 @@ pub(crate) fn paragraph<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, Paragraph<'s>> { - let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; + let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; let contexts = [ ContextElement::HasAffiliatedKeyword(HasAffiliatedKeywordInner { - start_after_affiliated_keywords: input, + start_after_affiliated_keywords: remaining, keywords: &affiliated_keywords, }), ContextElement::ExitMatcherNode(ExitMatcherNode { @@ -52,10 +53,12 @@ pub(crate) fn paragraph<'b, 'g, 'r, 's>( let (remaining, (children, _exit_contents)) = verify( many_till(standard_set_object_matcher, exit_matcher), |(children, _exit_contents)| !children.is_empty(), - )(input)?; + )(remaining)?; // Not checking parent exit matcher because if there are any children matched then we have a valid paragraph. + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; let source = get_consumed(input, remaining); Ok(( diff --git a/src/parser/plain_list.rs b/src/parser/plain_list.rs index f33ba0d..7aee12a 100644 --- a/src/parser/plain_list.rs +++ b/src/parser/plain_list.rs @@ -81,7 +81,7 @@ pub(crate) fn plain_list<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, PlainList<'s>> { - let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; + let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; let contexts = [ ContextElement::Context("plain list"), @@ -99,7 +99,7 @@ pub(crate) fn plain_list<'b, 'g, 'r, 's>( let mut children = Vec::new(); let mut first_item_indentation: Option = None; let mut first_item_list_type: Option = None; - let mut remaining = input; + let mut remaining = remaining; // The final list item does not consume trailing blank lines (which instead get consumed by the list). We have three options here: // @@ -150,6 +150,8 @@ pub(crate) fn plain_list<'b, 'g, 'r, 's>( parser_with_context!(plain_list_item)(&final_item_context)(final_child_start)?; children.push((final_child_start, reparsed_final_item)); + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; let source = get_consumed(input, remaining); Ok(( remaining, diff --git a/src/parser/table.rs b/src/parser/table.rs index 2fc7967..695259f 100644 --- a/src/parser/table.rs +++ b/src/parser/table.rs @@ -19,6 +19,7 @@ use super::object_parser::table_cell_set_object; use super::org_source::OrgSource; use super::util::exit_matcher_parser; use super::util::get_name; +use super::util::maybe_consume_trailing_whitespace_if_not_exiting; use super::util::org_line_ending; use crate::context::parser_with_context; use crate::context::ContextElement; @@ -40,9 +41,9 @@ pub(crate) fn org_mode_table<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, Table<'s>> { - let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; - start_of_line(input)?; - peek(tuple((space0, tag("|"))))(input)?; + let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?; + start_of_line(remaining)?; + peek(tuple((space0, tag("|"))))(remaining)?; let contexts = [ ContextElement::ConsumeTrailingWhitespace(true), @@ -60,11 +61,13 @@ pub(crate) fn org_mode_table<'b, 'g, 'r, 's>( let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); let (remaining, (children, _exit_contents)) = - many_till(org_mode_table_row_matcher, exit_matcher)(input)?; + many_till(org_mode_table_row_matcher, exit_matcher)(remaining)?; let (remaining, formulas) = many0(parser_with_context!(table_formula_keyword)(context))(remaining)?; + let (remaining, _trailing_ws) = + maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; let source = get_consumed(input, remaining); Ok(( diff --git a/src/types/lesser_element.rs b/src/types/lesser_element.rs index ee126d6..bf2ac27 100644 --- a/src/types/lesser_element.rs +++ b/src/types/lesser_element.rs @@ -149,6 +149,7 @@ pub struct BabelCall<'s> { pub struct LatexEnvironment<'s> { pub source: &'s str, pub name: Option<&'s str>, + pub value: &'s str, } /// A line number used in switches to lesser blocks.