From a32cea813919f9ebf7d21afda0f07bd276cc1e25 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 8 Oct 2023 15:08:21 -0400 Subject: [PATCH] Coalesce whitespace in macro args. --- src/compare/compare_field.rs | 10 ++++---- src/compare/diff.rs | 2 +- src/types/object.rs | 10 ++++++++ src/types/util.rs | 46 ++++++++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 5 deletions(-) diff --git a/src/compare/compare_field.rs b/src/compare/compare_field.rs index 319ba7fd..3173ea8e 100644 --- a/src/compare/compare_field.rs +++ b/src/compare/compare_field.rs @@ -108,10 +108,10 @@ pub(crate) fn compare_property_list_of_quoted_string< 'b, 's, 'x, - 'y, R, - RV: AsRef + std::fmt::Debug + 'y, - RG: Fn(R) -> Option<&'y Vec>, + RV: AsRef + std::fmt::Debug, + RI: Iterator, + RG: Fn(R) -> Option, >( emacs: &'b Token<'s>, rust_node: R, @@ -122,7 +122,9 @@ pub(crate) fn compare_property_list_of_quoted_string< .map(Token::as_list) .map_or(Ok(None), |r| r.map(Some))?; let rust_value = rust_value_getter(rust_node); - match (value, rust_value) { + // TODO: Seems we are needlessly coverting to a vec here. + let rust_value: Option> = rust_value.map(|it| it.collect()); + match (value, &rust_value) { (None, None) => {} (None, Some(_)) | (Some(_), None) => { let this_status = DiffStatus::Bad; diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 3c6da09f..137441e1 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -3090,7 +3090,7 @@ fn compare_org_macro<'b, 's>( |r| if r.macro_args.is_empty() { None } else { - Some(&r.macro_args) + Some(r.get_macro_args()) }, compare_property_list_of_quoted_string ) diff --git a/src/types/object.rs b/src/types/object.rs index f27324fb..1fd8539d 100644 --- a/src/types/object.rs +++ b/src/types/object.rs @@ -1,6 +1,7 @@ use std::borrow::Borrow; use std::borrow::Cow; +use super::util::coalesce_whitespace; use super::util::coalesce_whitespace_if_line_break; use super::util::remove_line_break; use super::util::remove_whitespace_if_line_break; @@ -149,6 +150,9 @@ pub struct AngleLink<'s> { pub struct OrgMacro<'s> { pub source: &'s str, pub macro_name: &'s str, + /// The macro args from the source. + /// + /// This does not take into account the post-processing that you would get from the upstream emacs org-mode AST. Use `get_macro_args` for an equivalent value. pub macro_args: Vec<&'s str>, pub macro_value: &'s str, } @@ -733,3 +737,9 @@ impl<'s> AngleLink<'s> { self.search_option.map(remove_whitespace_if_line_break) } } + +impl<'s> OrgMacro<'s> { + pub fn get_macro_args<'b>(&'b self) -> impl Iterator> + 'b { + self.macro_args.iter().map(|arg| coalesce_whitespace(*arg)) + } +} diff --git a/src/types/util.rs b/src/types/util.rs index c336474a..bb7deda4 100644 --- a/src/types/util.rs +++ b/src/types/util.rs @@ -197,3 +197,49 @@ enum CoalesceWhitespaceIfLineBreakState { ret: String, }, } + +/// Removes all whitespace from a string. +/// +/// Example: "foo bar" => "foobar" and "foo \n bar" => "foobar". +pub(crate) fn coalesce_whitespace<'s>(input: &'s str) -> Cow<'s, str> { + let mut state = CoalesceWhitespace::Normal; + for (offset, c) in input.char_indices() { + match (&mut state, c) { + (CoalesceWhitespace::Normal, ' ' | '\t' | '\r' | '\n') => { + let mut ret = String::with_capacity(input.len()); + ret.push_str(&input[..offset]); + ret.push(' '); + state = CoalesceWhitespace::HasWhitespace { + in_whitespace: true, + ret, + }; + } + (CoalesceWhitespace::Normal, _) => {} + ( + CoalesceWhitespace::HasWhitespace { in_whitespace, ret }, + ' ' | '\t' | '\r' | '\n', + ) => { + if !*in_whitespace { + *in_whitespace = true; + ret.push(' '); + } + } + (CoalesceWhitespace::HasWhitespace { in_whitespace, ret }, _) => { + *in_whitespace = false; + ret.push(c); + } + } + } + match state { + CoalesceWhitespace::Normal => Cow::Borrowed(input), + CoalesceWhitespace::HasWhitespace { + in_whitespace: _, + ret, + } => Cow::Owned(ret), + } +} + +enum CoalesceWhitespace { + Normal, + HasWhitespace { in_whitespace: bool, ret: String }, +}