From ba55e0df4f7f100f8c03245096002ca024d7989e Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 8 Oct 2023 13:23:12 -0400 Subject: [PATCH] Remove all whitespace from search option if it contains any line breaks and handle triple+ slashes for file links. --- src/compare/diff.rs | 2 +- src/parser/angle_link.rs | 8 +++++++ src/types/object.rs | 46 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/src/compare/diff.rs b/src/compare/diff.rs index c4a3967..2f26f22 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -3044,7 +3044,7 @@ fn compare_angle_link<'b, 's>( ), ( EmacsField::Required(":search-option"), - |r| r.search_option, + |r| r.get_search_option(), compare_property_quoted_string ) )? { diff --git a/src/parser/angle_link.rs b/src/parser/angle_link.rs index 46a55fa..cffa533 100644 --- a/src/parser/angle_link.rs +++ b/src/parser/angle_link.rs @@ -1,13 +1,17 @@ use nom::branch::alt; use nom::bytes::complete::tag; +use nom::bytes::complete::take; use nom::bytes::complete::take_until; use nom::combinator::consumed; +use nom::combinator::flat_map; use nom::combinator::map; use nom::combinator::map_parser; use nom::combinator::opt; use nom::combinator::peek; use nom::combinator::recognize; use nom::combinator::rest; +use nom::combinator::verify; +use nom::multi::many1_count; use nom::sequence::tuple; use super::org_source::OrgSource; @@ -114,6 +118,10 @@ fn parse_file_angle_link<'b, 'g, 'r, 's>( )), |(_, application, _)| application, )(input)?; + let (remaining, _) = opt(flat_map( + peek(map(verify(many1_count(tag("/")), |c| *c >= 3), |c| c - 1)), + take, + ))(remaining)?; let (remaining, path) = alt((take_until("::"), rest))(remaining)?; let (remaining, search_option) = opt(map(tuple((tag("::"), rest)), |(_, search_option)| { search_option diff --git a/src/types/object.rs b/src/types/object.rs index 1eb54ab..9b52c53 100644 --- a/src/types/object.rs +++ b/src/types/object.rs @@ -738,6 +738,12 @@ enum PathState { HasLineBreak(String), } +enum SearchOptionState { + Normal, + HasWhitespace(usize), + HasLineBreak(String), +} + impl<'s> AngleLink<'s> { /// Remove line breaks but preserve multiple consecutive spaces. pub fn get_path(&self) -> Cow<'s, str> { @@ -761,4 +767,44 @@ impl<'s> AngleLink<'s> { PathState::HasLineBreak(ret) => Cow::Owned(ret), } } + + /// Remove all whitespace but only if search_option contains a line break. + pub fn get_search_option(&self) -> Option> { + self.search_option.map(|search_option| { + let mut state = SearchOptionState::Normal; + for (offset, c) in search_option.char_indices() { + match (&mut state, c) { + (SearchOptionState::Normal, '\n') => { + let mut ret = String::with_capacity(search_option.len()); + ret.push_str(&search_option[..offset]); + state = SearchOptionState::HasLineBreak(ret); + } + (SearchOptionState::Normal, ' ' | '\t') => { + state = SearchOptionState::HasWhitespace(offset); + } + (SearchOptionState::Normal, _) => {} + (SearchOptionState::HasWhitespace(first_whitespace_offset), '\n') => { + let mut ret = String::with_capacity(search_option.len()); + ret.push_str(&search_option[..*first_whitespace_offset]); + for c in search_option[*first_whitespace_offset..offset].chars() { + if !c.is_ascii_whitespace() { + ret.push(c); + } + } + state = SearchOptionState::HasLineBreak(ret); + } + (SearchOptionState::HasWhitespace(_), _) => {} + (SearchOptionState::HasLineBreak(_), ' ' | '\t' | '\r' | '\n') => {} + (SearchOptionState::HasLineBreak(ret), _) => { + ret.push(c); + } + } + } + match state { + SearchOptionState::Normal => Cow::Borrowed(search_option), + SearchOptionState::HasWhitespace(_) => Cow::Borrowed(search_option), + SearchOptionState::HasLineBreak(ret) => Cow::Owned(ret), + } + }) + } }