From baa62c609e421c4518070958df9ef721c20857e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Campinas?= Date: Thu, 10 Jan 2019 14:25:07 +0100 Subject: [PATCH] recognize strings inside comments in order to avoid indenting them Close #3270 --- src/comment.rs | 84 +++++++++++++++++++++++++++------ src/utils.rs | 15 ++++-- tests/source/issue-3132.rs | 13 ----- tests/source/issue-3270.rs | 10 ++++ tests/target/issue-3132.rs | 10 ++-- tests/target/issue-3270-wrap.rs | 10 ++++ tests/target/issue-3270.rs | 10 ++++ 7 files changed, 114 insertions(+), 38 deletions(-) delete mode 100644 tests/source/issue-3132.rs create mode 100644 tests/source/issue-3270.rs create mode 100644 tests/target/issue-3270-wrap.rs create mode 100644 tests/target/issue-3270.rs diff --git a/src/comment.rs b/src/comment.rs index 492b03c045a3..ef63bf464bc7 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -1029,21 +1029,28 @@ impl RichChar for (usize, char) { #[derive(PartialEq, Eq, Debug, Clone, Copy)] enum CharClassesStatus { Normal, + /// Character is within a string LitString, LitStringEscape, + /// Character is within a raw string LitRawString(u32), RawStringPrefix(u32), RawStringSuffix(u32), LitChar, LitCharEscape, - // The u32 is the nesting deepness of the comment + /// Character inside a block comment, with the integer indicating the nesting deepness of the + /// comment BlockComment(u32), - // Status when the '/' has been consumed, but not yet the '*', deepness is - // the new deepness (after the comment opening). + /// Character inside a block-commented string, with the integer indicating the nesting deepness + /// of the comment + StringInBlockComment(u32), + /// Status when the '/' has been consumed, but not yet the '*', deepness is + /// the new deepness (after the comment opening). BlockCommentOpening(u32), - // Status when the '*' has been consumed, but not yet the '/', deepness is - // the new deepness (after the comment closing). + /// Status when the '*' has been consumed, but not yet the '/', deepness is + /// the new deepness (after the comment closing). BlockCommentClosing(u32), + /// Character is within a line comment LineComment, } @@ -1067,6 +1074,12 @@ pub enum FullCodeCharKind { InComment, /// Last character of a comment, '\n' for a line comment, '/' for a block comment. EndComment, + /// Start of a mutlitine string inside a comment + StartStringCommented, + /// End of a mutlitine string inside a comment + EndStringCommented, + /// Inside a commented string + InStringCommented, /// Start of a mutlitine string StartString, /// End of a mutlitine string @@ -1080,7 +1093,21 @@ impl FullCodeCharKind { match self { FullCodeCharKind::StartComment | FullCodeCharKind::InComment - | FullCodeCharKind::EndComment => true, + | FullCodeCharKind::EndComment + | FullCodeCharKind::StartStringCommented + | FullCodeCharKind::InStringCommented + | FullCodeCharKind::EndStringCommented => true, + _ => false, + } + } + + /// Returns true if the character is inside a comment + pub fn inside_comment(self) -> bool { + match self { + FullCodeCharKind::InComment + | FullCodeCharKind::StartStringCommented + | FullCodeCharKind::InStringCommented + | FullCodeCharKind::EndStringCommented => true, _ => false, } } @@ -1089,6 +1116,12 @@ impl FullCodeCharKind { self == FullCodeCharKind::InString || self == FullCodeCharKind::StartString } + /// Returns true if the character is within a commented string + pub fn is_commented_string(self) -> bool { + self == FullCodeCharKind::InStringCommented + || self == FullCodeCharKind::StartStringCommented + } + fn to_codecharkind(self) -> CodeCharKind { if self.is_comment() { CodeCharKind::Comment @@ -1232,18 +1265,27 @@ where }, _ => CharClassesStatus::Normal, }, + CharClassesStatus::StringInBlockComment(deepness) => { + char_kind = FullCodeCharKind::InStringCommented; + if chr == '"' { + CharClassesStatus::BlockComment(deepness) + } else { + CharClassesStatus::StringInBlockComment(deepness) + } + } CharClassesStatus::BlockComment(deepness) => { assert_ne!(deepness, 0); - self.status = match self.base.peek() { + char_kind = FullCodeCharKind::InComment; + match self.base.peek() { Some(next) if next.get_char() == '/' && chr == '*' => { CharClassesStatus::BlockCommentClosing(deepness - 1) } Some(next) if next.get_char() == '*' && chr == '/' => { CharClassesStatus::BlockCommentOpening(deepness + 1) } - _ => CharClassesStatus::BlockComment(deepness), - }; - return Some((FullCodeCharKind::InComment, item)); + _ if chr == '"' => CharClassesStatus::StringInBlockComment(deepness), + _ => self.status, + } } CharClassesStatus::BlockCommentOpening(deepness) => { assert_eq!(chr, '*'); @@ -1299,26 +1341,33 @@ impl<'a> Iterator for LineClasses<'a> { let mut line = String::new(); - let start_class = match self.base.peek() { + let start_kind = match self.base.peek() { Some((kind, _)) => *kind, None => unreachable!(), }; while let Some((kind, c)) = self.base.next() { + // needed to set the kind of the ending character on the last line + self.kind = kind; if c == '\n' { - self.kind = match (start_class, kind) { + self.kind = match (start_kind, kind) { (FullCodeCharKind::Normal, FullCodeCharKind::InString) => { FullCodeCharKind::StartString } (FullCodeCharKind::InString, FullCodeCharKind::Normal) => { FullCodeCharKind::EndString } + (FullCodeCharKind::InComment, FullCodeCharKind::InStringCommented) => { + FullCodeCharKind::StartStringCommented + } + (FullCodeCharKind::InStringCommented, FullCodeCharKind::InComment) => { + FullCodeCharKind::EndStringCommented + } _ => kind, }; break; - } else { - line.push(c); } + line.push(c); } // Workaround for CRLF newline. @@ -1364,7 +1413,12 @@ impl<'a> Iterator for UngroupedCommentCodeSlices<'a> { } FullCodeCharKind::StartComment => { // Consume the whole comment - while let Some((FullCodeCharKind::InComment, (_, _))) = self.iter.next() {} + loop { + match self.iter.next() { + Some((kind, ..)) if kind.inside_comment() => continue, + _ => break, + } + } } _ => panic!(), } diff --git a/src/utils.rs b/src/utils.rs index a6a46df6ddc1..dd480f9788fb 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -527,8 +527,10 @@ pub fn trim_left_preserve_layout(orig: &str, indent: Indent, config: &Config) -> Some(get_prefix_space_width(config, &line)) }; - let line = if veto_trim || (kind.is_string() && !line.ends_with('\\')) { - veto_trim = kind.is_string() && !line.ends_with('\\'); + let new_veto_trim_value = + (kind.is_string() || kind.is_commented_string()) && !line.ends_with('\\'); + let line = if veto_trim || new_veto_trim_value { + veto_trim = new_veto_trim_value; trimmed = false; line } else { @@ -536,10 +538,13 @@ pub fn trim_left_preserve_layout(orig: &str, indent: Indent, config: &Config) -> }; trimmed_lines.push((trimmed, line, prefix_space_width)); - // When computing the minimum, do not consider lines within a string. - // The reason is there is a veto against trimming and indenting such lines + // Because there is a veto against trimming and indenting lines within a string, + // such lines should not be taken into account when computing the minimum. match kind { - FullCodeCharKind::InString | FullCodeCharKind::EndString => None, + FullCodeCharKind::InString + | FullCodeCharKind::EndString + | FullCodeCharKind::InStringCommented + | FullCodeCharKind::EndStringCommented => None, _ => prefix_space_width, } }) diff --git a/tests/source/issue-3132.rs b/tests/source/issue-3132.rs deleted file mode 100644 index a43b83223e23..000000000000 --- a/tests/source/issue-3132.rs +++ /dev/null @@ -1,13 +0,0 @@ -fn test() { - /* - a - */ - let x = 42; - /* - aaa - "line 1 - line 2 - line 3" - */ - let x = 42; -} diff --git a/tests/source/issue-3270.rs b/tests/source/issue-3270.rs new file mode 100644 index 000000000000..9b1a66a63b7c --- /dev/null +++ b/tests/source/issue-3270.rs @@ -0,0 +1,10 @@ +pub fn main() { + /* let s = String::from( + " +hello +world +", + ); */ + + assert_eq!(s, "\nhello\nworld\n"); +} diff --git a/tests/target/issue-3132.rs b/tests/target/issue-3132.rs index 42388e09f746..c3d24fc10f6c 100644 --- a/tests/target/issue-3132.rs +++ b/tests/target/issue-3132.rs @@ -4,10 +4,10 @@ fn test() { */ let x = 42; /* - aaa - "line 1 - line 2 - line 3" - */ + aaa + "line 1 + line 2 + line 3" + */ let x = 42; } diff --git a/tests/target/issue-3270-wrap.rs b/tests/target/issue-3270-wrap.rs new file mode 100644 index 000000000000..e27f71aa32f5 --- /dev/null +++ b/tests/target/issue-3270-wrap.rs @@ -0,0 +1,10 @@ +// rustfmt-wrap_comments: true + +fn func() { + let x = 42; + /* + let something = "one line line line line line line line line line line line line + two lines + three lines"; + */ +} diff --git a/tests/target/issue-3270.rs b/tests/target/issue-3270.rs new file mode 100644 index 000000000000..4f624f1193b4 --- /dev/null +++ b/tests/target/issue-3270.rs @@ -0,0 +1,10 @@ +pub fn main() { + /* let s = String::from( + " +hello +world +", + ); */ + + assert_eq!(s, "\nhello\nworld\n"); +}