From c3edf6d3a1f72088a4343948f6aafe1fc74a3208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Campinas?= Date: Mon, 17 Sep 2018 23:17:36 +0200 Subject: [PATCH] Fix indent computation of a macro with braces. The leading whitespace of a multine string was taken into account when computing the `min_prefix_space_width`, even if that line couldn't be trimmed. The consequence is it was always shifting the macro's content to the right. --- src/comment.rs | 43 +++++++--- src/macros.rs | 10 ++- tests/source/issue-2973.rs | 158 +++++++++++++++++++++++++++++++++++++ tests/target/issue-2973.rs | 158 +++++++++++++++++++++++++++++++++++++ 4 files changed, 354 insertions(+), 15 deletions(-) create mode 100644 tests/source/issue-2973.rs create mode 100644 tests/target/issue-2973.rs diff --git a/src/comment.rs b/src/comment.rs index a48012d411ec0..17ec29c3cbc57 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -821,6 +821,10 @@ pub enum FullCodeCharKind { InComment, /// Last character of a comment, '\n' for a line comment, '/' for a block comment. EndComment, + /// Start of a mutlitine string + StartString, + /// End of a mutlitine string + EndString, /// Inside a string. InString, } @@ -836,7 +840,7 @@ impl FullCodeCharKind { } pub fn is_string(self) -> bool { - self == FullCodeCharKind::InString + self == FullCodeCharKind::InString || self == FullCodeCharKind::StartString } fn to_codecharkind(self) -> CodeCharKind { @@ -924,17 +928,14 @@ where _ => CharClassesStatus::Normal, // Unreachable } } - CharClassesStatus::LitString => match chr { - '"' => CharClassesStatus::Normal, - '\\' => { - char_kind = FullCodeCharKind::InString; - CharClassesStatus::LitStringEscape - } - _ => { - char_kind = FullCodeCharKind::InString; - CharClassesStatus::LitString + CharClassesStatus::LitString => { + char_kind = FullCodeCharKind::InString; + match chr { + '"' => CharClassesStatus::Normal, + '\\' => CharClassesStatus::LitStringEscape, + _ => CharClassesStatus::LitString, } - }, + } CharClassesStatus::LitStringEscape => { char_kind = FullCodeCharKind::InString; CharClassesStatus::LitString @@ -1052,9 +1053,22 @@ impl<'a> Iterator for LineClasses<'a> { let mut line = String::new(); + let start_class = match self.base.peek() { + Some((kind, _)) => *kind, + None => FullCodeCharKind::Normal, + }; + while let Some((kind, c)) = self.base.next() { - self.kind = kind; if c == '\n' { + self.kind = match (start_class, kind) { + (FullCodeCharKind::Normal, FullCodeCharKind::InString) => { + FullCodeCharKind::StartString + } + (FullCodeCharKind::InString, FullCodeCharKind::Normal) => { + FullCodeCharKind::EndString + } + _ => kind, + }; break; } else { line.push(c); @@ -1227,7 +1241,10 @@ pub fn recover_comment_removed( pub fn filter_normal_code(code: &str) -> String { let mut buffer = String::with_capacity(code.len()); LineClasses::new(code).for_each(|(kind, line)| match kind { - FullCodeCharKind::Normal | FullCodeCharKind::InString => { + FullCodeCharKind::Normal + | FullCodeCharKind::StartString + | FullCodeCharKind::InString + | FullCodeCharKind::EndString => { buffer.push_str(&line); buffer.push('\n'); } diff --git a/src/macros.rs b/src/macros.rs index 8eff489412b31..e68a725e15fe5 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1118,6 +1118,7 @@ fn indent_macro_snippet( } else { Some(get_prefix_space_width(context, &line)) }; + let line = if veto_trim || (kind.is_string() && !line.ends_with('\\')) { veto_trim = kind.is_string() && !line.ends_with('\\'); trimmed = false; @@ -1126,7 +1127,12 @@ fn indent_macro_snippet( line.trim().to_owned() }; trimmed_lines.push((trimmed, line, prefix_space_width)); - prefix_space_width + + // when computing the minimum, do not consider lines within a string + match kind { + FullCodeCharKind::InString | FullCodeCharKind::EndString => None, + _ => prefix_space_width, + } }).min()?; Some( @@ -1139,7 +1145,7 @@ fn indent_macro_snippet( let new_indent_width = indent.width() + original_indent_width .saturating_sub(min_prefix_space_width); let new_indent = Indent::from_width(context.config, new_indent_width); - format!("{}{}", new_indent.to_string(context.config), line.trim()) + format!("{}{}", new_indent.to_string(context.config), line) } None => String::new(), }, diff --git a/tests/source/issue-2973.rs b/tests/source/issue-2973.rs new file mode 100644 index 0000000000000..5256dd7c9601e --- /dev/null +++ b/tests/source/issue-2973.rs @@ -0,0 +1,158 @@ +#[cfg(test)] +mod test { + summary_test! { + tokenize_recipe_interpolation_eol, + "foo: # some comment + {{hello}} +", + "foo: \ + {{hello}} \ +{{ahah}}", + "N:#$>^{N}$<.", + } + + summary_test! { + tokenize_strings, + r#"a = "'a'" + '"b"' + "'c'" + '"d"'#echo hello"#, + r#"N="+'+"+'#."#, + } + + summary_test! { + tokenize_recipe_interpolation_eol, + "foo: # some comment + {{hello}} +", + "N:#$>^{N}$<.", + } + + summary_test! { + tokenize_recipe_interpolation_eof, + "foo: # more comments + {{hello}} +# another comment +", + "N:#$>^{N}$<#$.", + } + + summary_test! { + tokenize_recipe_complex_interpolation_expression, + "foo: #lol\n {{a + b + \"z\" + blarg}}", + "N:#$>^{N+N+\"+N}<.", + } + + summary_test! { + tokenize_recipe_multiple_interpolations, + "foo:,#ok\n {{a}}0{{b}}1{{c}}", + "N:,#$>^{N}_{N}_{N}<.", + } + + summary_test! { + tokenize_junk, + "bob + +hello blah blah blah : a b c #whatever + ", + "N$$NNNN:NNN#$.", + } + + summary_test! { + tokenize_empty_lines, + " +# this does something +hello: + asdf + bsdf + + csdf + + dsdf # whatever + +# yolo + ", + "$#$N:$>^_$^_$$^_$$^_$$<#$.", + } + + summary_test! { + tokenize_comment_before_variable, + " +# +A='1' +echo: + echo {{A}} + ", + "$#$N='$N:$>^_{N}$<.", + } + + summary_test! { + tokenize_interpolation_backticks, + "hello:\n echo {{`echo hello` + `echo goodbye`}}", + "N:$>^_{`+`}<.", + } + + summary_test! { + tokenize_assignment_backticks, + "a = `echo hello` + `echo goodbye`", + "N=`+`.", + } + + summary_test! { + tokenize_multiple, + " +hello: + a + b + + c + + d + +# hello +bob: + frank + ", + + "$N:$>^_$^_$$^_$$^_$$<#$N:$>^_$<.", + } + + summary_test! { + tokenize_comment, + "a:=#", + "N:=#." + } + + summary_test! { + tokenize_comment_with_bang, + "a:=#foo!", + "N:=#." + } + + summary_test! { + tokenize_order, + r" +b: a + @mv a b + +a: + @touch F + @touch a + +d: c + @rm c + +c: b + @mv b c", + "$N:N$>^_$$^_$^_$$^_$$^_<.", + } + + summary_test! { + tokenize_parens, + r"((())) )abc(+", + "((())))N(+.", + } + + summary_test! { + crlf_newline, + "#\r\n#asdf\r\n", + "#$#$.", + } +} diff --git a/tests/target/issue-2973.rs b/tests/target/issue-2973.rs new file mode 100644 index 0000000000000..86574bd866860 --- /dev/null +++ b/tests/target/issue-2973.rs @@ -0,0 +1,158 @@ +#[cfg(test)] +mod test { + summary_test! { + tokenize_recipe_interpolation_eol, + "foo: # some comment + {{hello}} +", + "foo: \ + {{hello}} \ + {{ahah}}", + "N:#$>^{N}$<.", + } + + summary_test! { + tokenize_strings, + r#"a = "'a'" + '"b"' + "'c'" + '"d"'#echo hello"#, + r#"N="+'+"+'#."#, + } + + summary_test! { + tokenize_recipe_interpolation_eol, + "foo: # some comment + {{hello}} +", + "N:#$>^{N}$<.", + } + + summary_test! { + tokenize_recipe_interpolation_eof, + "foo: # more comments + {{hello}} +# another comment +", + "N:#$>^{N}$<#$.", + } + + summary_test! { + tokenize_recipe_complex_interpolation_expression, + "foo: #lol\n {{a + b + \"z\" + blarg}}", + "N:#$>^{N+N+\"+N}<.", + } + + summary_test! { + tokenize_recipe_multiple_interpolations, + "foo:,#ok\n {{a}}0{{b}}1{{c}}", + "N:,#$>^{N}_{N}_{N}<.", + } + + summary_test! { + tokenize_junk, + "bob + +hello blah blah blah : a b c #whatever + ", + "N$$NNNN:NNN#$.", + } + + summary_test! { + tokenize_empty_lines, + " +# this does something +hello: + asdf + bsdf + + csdf + + dsdf # whatever + +# yolo + ", + "$#$N:$>^_$^_$$^_$$^_$$<#$.", + } + + summary_test! { + tokenize_comment_before_variable, + " +# +A='1' +echo: + echo {{A}} + ", + "$#$N='$N:$>^_{N}$<.", + } + + summary_test! { + tokenize_interpolation_backticks, + "hello:\n echo {{`echo hello` + `echo goodbye`}}", + "N:$>^_{`+`}<.", + } + + summary_test! { + tokenize_assignment_backticks, + "a = `echo hello` + `echo goodbye`", + "N=`+`.", + } + + summary_test! { + tokenize_multiple, + " +hello: + a + b + + c + + d + +# hello +bob: + frank + ", + + "$N:$>^_$^_$$^_$$^_$$<#$N:$>^_$<.", + } + + summary_test! { + tokenize_comment, + "a:=#", + "N:=#." + } + + summary_test! { + tokenize_comment_with_bang, + "a:=#foo!", + "N:=#." + } + + summary_test! { + tokenize_order, + r" +b: a + @mv a b + +a: + @touch F + @touch a + +d: c + @rm c + +c: b + @mv b c", + "$N:N$>^_$$^_$^_$$^_$$^_<.", + } + + summary_test! { + tokenize_parens, + r"((())) )abc(+", + "((())))N(+.", + } + + summary_test! { + crlf_newline, + "#\r\n#asdf\r\n", + "#$#$.", + } +}