diff --git a/org_mode_samples/object/macro/escape.org b/org_mode_samples/object/macro/escape.org new file mode 100644 index 0000000..312f3d4 --- /dev/null +++ b/org_mode_samples/object/macro/escape.org @@ -0,0 +1,7 @@ +{{{foo}}} + +{{{fo\o}}} + +{{{foo(b\ar)}}} + +{{{foo(b\,r)}}} diff --git a/src/compare/compare_field.rs b/src/compare/compare_field.rs index 3173ea8..8cc4138 100644 --- a/src/compare/compare_field.rs +++ b/src/compare/compare_field.rs @@ -149,8 +149,8 @@ pub(crate) fn compare_property_list_of_quoted_string< if e != r { let this_status = DiffStatus::Bad; let message = Some(format!( - "{} mismatch (emacs != rust) {:?} != {:?}", - emacs_field, value, rust_value + "{} mismatch (emacs != rust) {:?} != {:?}. Full list: {:?} != {:?}", + emacs_field, e, r, value, rust_value )); return Ok(Some((this_status, message))); } diff --git a/src/types/util.rs b/src/types/util.rs index bb7deda..e8980ea 100644 --- a/src/types/util.rs +++ b/src/types/util.rs @@ -243,3 +243,149 @@ enum CoalesceWhitespace { Normal, HasWhitespace { in_whitespace: bool, ret: String }, } + +/// Removes all whitespace from a string and handle escaping characters. +/// +/// Example: "foo bar" => "foobar" and "foo \n bar" => "foobar" but if the escape character is backslash and comma is an escapable character than "foo\,bar" becomes "foo,bar". +pub(crate) fn coalesce_whitespace_escaped<'c, C: Fn(char) -> bool>( + escape_character: char, + escapable_characters: C, +) -> impl for<'s> Fn(&'s str) -> Cow<'s, str> { + move |input| impl_coalesce_whitespace_escaped(input, escape_character, &escapable_characters) +} + +fn impl_coalesce_whitespace_escaped<'s, C: Fn(char) -> bool>( + input: &'s str, + escape_character: char, + escapable_characters: C, +) -> Cow<'s, str> { + let mut state = CoalesceWhitespaceEscaped::Normal; + for (offset, c) in input.char_indices() { + state = match (state, c) { + (CoalesceWhitespaceEscaped::Normal, c) if c == escape_character => { + CoalesceWhitespaceEscaped::NormalEscaping { + escape_offset: offset, + } + } + (CoalesceWhitespaceEscaped::Normal, ' ' | '\t' | '\r' | '\n') => { + let mut ret = String::with_capacity(input.len()); + ret.push_str(&input[..offset]); + ret.push(' '); + CoalesceWhitespaceEscaped::RequiresMutation { + in_whitespace: true, + ret, + } + } + (CoalesceWhitespaceEscaped::Normal, _) => CoalesceWhitespaceEscaped::Normal, + (CoalesceWhitespaceEscaped::NormalEscaping { escape_offset }, c) + if escapable_characters(c) => + { + // We escaped a character so we need mutation + let mut ret = String::with_capacity(input.len()); + ret.push_str(&input[..escape_offset]); + ret.push(c); + CoalesceWhitespaceEscaped::RequiresMutation { + in_whitespace: false, + ret, + } + } + + ( + CoalesceWhitespaceEscaped::NormalEscaping { escape_offset: _ }, + ' ' | '\t' | '\r' | '\n', + ) => { + // We didn't escape the character but we hit whitespace anyway. + let mut ret = String::with_capacity(input.len()); + ret.push_str(&input[..offset]); + ret.push(' '); + CoalesceWhitespaceEscaped::RequiresMutation { + in_whitespace: true, + ret, + } + } + (CoalesceWhitespaceEscaped::NormalEscaping { escape_offset: _ }, _) => { + // We didn't escape the character so continue as normal. + CoalesceWhitespaceEscaped::Normal + } + + ( + CoalesceWhitespaceEscaped::RequiresMutation { + in_whitespace: _, + ret, + }, + c, + ) if c == escape_character => { + CoalesceWhitespaceEscaped::RequiresMutationEscaping { ret } + } + ( + CoalesceWhitespaceEscaped::RequiresMutation { + mut in_whitespace, + mut ret, + }, + ' ' | '\t' | '\r' | '\n', + ) => { + if !in_whitespace { + in_whitespace = true; + ret.push(' '); + } + CoalesceWhitespaceEscaped::RequiresMutation { in_whitespace, ret } + } + ( + CoalesceWhitespaceEscaped::RequiresMutation { + in_whitespace, + mut ret, + }, + _, + ) => { + ret.push(c); + CoalesceWhitespaceEscaped::RequiresMutation { + in_whitespace: false, + ret, + } + } + (CoalesceWhitespaceEscaped::RequiresMutationEscaping { mut ret }, c) + if escapable_characters(c) => + { + ret.push(c); + CoalesceWhitespaceEscaped::RequiresMutation { + in_whitespace: false, + ret, + } + } + ( + CoalesceWhitespaceEscaped::RequiresMutationEscaping { mut ret }, + ' ' | '\t' | '\r' | '\n', + ) => { + ret.push(' '); + CoalesceWhitespaceEscaped::RequiresMutation { + in_whitespace: true, + ret, + } + } + (CoalesceWhitespaceEscaped::RequiresMutationEscaping { mut ret }, c) => { + ret.push(escape_character); + ret.push(c); + // TODO + CoalesceWhitespaceEscaped::RequiresMutation { + in_whitespace: false, + ret, + } + } + } + } + // match state { + // CoalesceWhitespaceEscaped::Normal => Cow::Borrowed(input), + // CoalesceWhitespaceEscaped::RequiresMutation { + // in_whitespace: _, + // ret, + // } => Cow::Owned(ret), + // } + todo!() +} + +enum CoalesceWhitespaceEscaped { + Normal, + NormalEscaping { escape_offset: usize }, + RequiresMutation { in_whitespace: bool, ret: String }, + RequiresMutationEscaping { ret: String }, +}