From e0d20ca4d6b6d9296144a3af436afeb4b56872d0 Mon Sep 17 00:00:00 2001 From: WhizSid Date: Sat, 27 Mar 2021 18:26:03 +0530 Subject: [PATCH] Ignoring code blocks when trimming comments --- src/formatting/comment.rs | 252 +++++++++++++++++++++++++++++++++++++ src/formatting/utils.rs | 3 +- tests/source/issue-4753.rs | 13 ++ tests/target/issue-4753.rs | 13 ++ 4 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 tests/source/issue-4753.rs create mode 100644 tests/target/issue-4753.rs diff --git a/src/formatting/comment.rs b/src/formatting/comment.rs index 6277e770fd2..6b08b36ddc4 100644 --- a/src/formatting/comment.rs +++ b/src/formatting/comment.rs @@ -1125,6 +1125,12 @@ enum CharClassesStatus { /// Character inside a block-commented string, with the integer indicating the nesting deepness /// of the comment StringInBlockComment(u32), + CodeBlockPrefix(u32, u32), + CodeBlockSuffix(u32, u32, u32), + /// Character inside a multiline code block, with the first integer indicating the number of + /// backticks used to open the block and the second integer indicating the nesting deepness of + /// the comment + CodeBlock(u32, u32), /// Status when the '/' has been consumed, but not yet the '*', deepness is /// the new deepness (after the comment opening). BlockCommentOpening(u32), @@ -1167,6 +1173,12 @@ pub(crate) enum FullCodeCharKind { EndString, /// Inside a string. InString, + /// Start of a multiline doc code block + StartCodeBlock, + /// Inside a multilne doc code block + InCodeBlock, + /// End of mutline doc code block + EndCodeBlock, } impl FullCodeCharKind { @@ -1179,6 +1191,9 @@ impl FullCodeCharKind { | FullCodeCharKind::StartStringCommented | FullCodeCharKind::InStringCommented | FullCodeCharKind::EndStringCommented + | FullCodeCharKind::StartCodeBlock + | FullCodeCharKind::InCodeBlock + | FullCodeCharKind::EndCodeBlock ) } @@ -1190,6 +1205,9 @@ impl FullCodeCharKind { | FullCodeCharKind::StartStringCommented | FullCodeCharKind::InStringCommented | FullCodeCharKind::EndStringCommented + | FullCodeCharKind::StartCodeBlock + | FullCodeCharKind::InCodeBlock + | FullCodeCharKind::EndCodeBlock ) } @@ -1202,6 +1220,10 @@ impl FullCodeCharKind { self == FullCodeCharKind::InStringCommented || self == FullCodeCharKind::StartStringCommented } + + pub(crate) fn is_code_block(self) -> bool { + self == FullCodeCharKind::InCodeBlock || self == FullCodeCharKind::StartCodeBlock + } } impl CharClasses @@ -1349,6 +1371,49 @@ where CharClassesStatus::StringInBlockComment(deepness) } } + CharClassesStatus::CodeBlock(backticks, deepness) => { + char_kind = FullCodeCharKind::InCodeBlock; + if chr == '`' && self.base.peek().map(RichChar::get_char) == Some('`') { + CharClassesStatus::CodeBlockSuffix(1, backticks, deepness) + } else if chr == '*' && self.base.peek().map(RichChar::get_char) == Some('/') { + char_kind = FullCodeCharKind::InComment; + CharClassesStatus::BlockCommentClosing(deepness - 1) + } else { + CharClassesStatus::CodeBlock(backticks, deepness) + } + } + CharClassesStatus::CodeBlockPrefix(backticks, deepness) => { + char_kind = FullCodeCharKind::InComment; + match chr { + '`' => CharClassesStatus::CodeBlockPrefix(backticks + 1, deepness), + '\n' if backticks > 2 => CharClassesStatus::CodeBlock(backticks, deepness), + '*' if self.base.peek().map(RichChar::get_char) == Some('/') => { + char_kind = FullCodeCharKind::InComment; + CharClassesStatus::BlockCommentClosing(deepness - 1) + } + _ if backticks < 3 => CharClassesStatus::BlockComment(deepness), + _ => CharClassesStatus::CodeBlockPrefix(backticks, deepness), + } + } + CharClassesStatus::CodeBlockSuffix(backticks, need_backticks, deepness) => { + char_kind = FullCodeCharKind::InCodeBlock; + match chr { + '`' => { + CharClassesStatus::CodeBlockSuffix(backticks + 1, need_backticks, deepness) + } + '\n' if backticks >= need_backticks => { + CharClassesStatus::BlockComment(deepness) + } + '*' if self.base.peek().map(RichChar::get_char) == Some('/') => { + char_kind = FullCodeCharKind::InComment; + CharClassesStatus::BlockCommentClosing(deepness - 1) + } + _ if char::is_whitespace(chr) && backticks >= need_backticks => { + CharClassesStatus::CodeBlockSuffix(backticks, need_backticks, deepness) + } + _ => CharClassesStatus::CodeBlock(need_backticks, deepness), + } + } CharClassesStatus::BlockComment(deepness) => { assert_ne!(deepness, 0); char_kind = FullCodeCharKind::InComment; @@ -1359,6 +1424,9 @@ where Some(next) if next.get_char() == '*' && chr == '/' => { CharClassesStatus::BlockCommentOpening(deepness + 1) } + Some(next) if next.get_char() == '`' && chr == '`' => { + CharClassesStatus::CodeBlockPrefix(1, deepness) + } _ if chr == '"' => CharClassesStatus::StringInBlockComment(deepness), _ => self.status, } @@ -1435,6 +1503,12 @@ impl<'a> Iterator for LineClasses<'a> { (FullCodeCharKind::InString, FullCodeCharKind::Normal) => { FullCodeCharKind::EndString } + (FullCodeCharKind::InComment, FullCodeCharKind::InCodeBlock) => { + FullCodeCharKind::StartCodeBlock + } + (FullCodeCharKind::InCodeBlock, FullCodeCharKind::InComment) => { + FullCodeCharKind::EndCodeBlock + } (FullCodeCharKind::InComment, FullCodeCharKind::InStringCommented) => { FullCodeCharKind::StartStringCommented } @@ -1745,6 +1819,184 @@ mod test { assert_eq!(None, iter.next()); } + #[test] + fn char_classes_code_block() { + fn check(cmnt: &str, kinds: &[(FullCodeCharKind, char)]) { + let mut iter = CharClasses::new(cmnt.chars()); + let mut kind_iter = kinds.iter(); + assert_eq!(cmnt.len(), kinds.len()); + + while let Some(((act_kind, act_char), (expt_kind, expt_char))) = + iter.next().zip(kind_iter.next()) + { + assert_eq!((expt_kind, expt_char), (&act_kind, &act_char)); + } + } + + check( + "/*\n````test\nc\n```\nc\n````\n */", + &[ + (FullCodeCharKind::StartComment, '/'), + (FullCodeCharKind::InComment, '*'), + (FullCodeCharKind::InComment, '\n'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, 't'), + (FullCodeCharKind::InComment, 'e'), + (FullCodeCharKind::InComment, 's'), + (FullCodeCharKind::InComment, 't'), + (FullCodeCharKind::InComment, '\n'), + (FullCodeCharKind::InCodeBlock, 'c'), + (FullCodeCharKind::InCodeBlock, '\n'), + (FullCodeCharKind::InCodeBlock, '`'), + (FullCodeCharKind::InCodeBlock, '`'), + (FullCodeCharKind::InCodeBlock, '`'), + (FullCodeCharKind::InCodeBlock, '\n'), + (FullCodeCharKind::InCodeBlock, 'c'), + (FullCodeCharKind::InCodeBlock, '\n'), + (FullCodeCharKind::InCodeBlock, '`'), + (FullCodeCharKind::InCodeBlock, '`'), + (FullCodeCharKind::InCodeBlock, '`'), + (FullCodeCharKind::InCodeBlock, '`'), + (FullCodeCharKind::InCodeBlock, '\n'), + (FullCodeCharKind::InComment, ' '), + (FullCodeCharKind::InComment, '*'), + (FullCodeCharKind::EndComment, '/'), + ], + ); + + check( + "/*\n``test\n``abc`\n```test\nc\n``a`\n```\n */", + &[ + (FullCodeCharKind::StartComment, '/'), + (FullCodeCharKind::InComment, '*'), + (FullCodeCharKind::InComment, '\n'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, 't'), + (FullCodeCharKind::InComment, 'e'), + (FullCodeCharKind::InComment, 's'), + (FullCodeCharKind::InComment, 't'), + (FullCodeCharKind::InComment, '\n'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, 'a'), + (FullCodeCharKind::InComment, 'b'), + (FullCodeCharKind::InComment, 'c'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '\n'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, 't'), + (FullCodeCharKind::InComment, 'e'), + (FullCodeCharKind::InComment, 's'), + (FullCodeCharKind::InComment, 't'), + (FullCodeCharKind::InComment, '\n'), + (FullCodeCharKind::InCodeBlock, 'c'), + (FullCodeCharKind::InCodeBlock, '\n'), + (FullCodeCharKind::InCodeBlock, '`'), + (FullCodeCharKind::InCodeBlock, '`'), + (FullCodeCharKind::InCodeBlock, 'a'), + (FullCodeCharKind::InCodeBlock, '`'), + (FullCodeCharKind::InCodeBlock, '\n'), + (FullCodeCharKind::InCodeBlock, '`'), + (FullCodeCharKind::InCodeBlock, '`'), + (FullCodeCharKind::InCodeBlock, '`'), + (FullCodeCharKind::InCodeBlock, '\n'), + (FullCodeCharKind::InComment, ' '), + (FullCodeCharKind::InComment, '*'), + (FullCodeCharKind::EndComment, '/'), + ], + ); + + check( + "/*\n```*/", + &[ + (FullCodeCharKind::StartComment, '/'), + (FullCodeCharKind::InComment, '*'), + (FullCodeCharKind::InComment, '\n'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '*'), + (FullCodeCharKind::EndComment, '/'), + ], + ); + + check( + "/*\n``*/", + &[ + (FullCodeCharKind::StartComment, '/'), + (FullCodeCharKind::InComment, '*'), + (FullCodeCharKind::InComment, '\n'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '*'), + (FullCodeCharKind::EndComment, '/'), + ], + ); + + check( + "/*\n```\n */", + &[ + (FullCodeCharKind::StartComment, '/'), + (FullCodeCharKind::InComment, '*'), + (FullCodeCharKind::InComment, '\n'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '\n'), + (FullCodeCharKind::InCodeBlock, ' '), + (FullCodeCharKind::InComment, '*'), + (FullCodeCharKind::EndComment, '/'), + ], + ); + + check( + "/*\n```\nc\n``*/", + &[ + (FullCodeCharKind::StartComment, '/'), + (FullCodeCharKind::InComment, '*'), + (FullCodeCharKind::InComment, '\n'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '\n'), + (FullCodeCharKind::InCodeBlock, 'c'), + (FullCodeCharKind::InCodeBlock, '\n'), + (FullCodeCharKind::InCodeBlock, '`'), + (FullCodeCharKind::InCodeBlock, '`'), + (FullCodeCharKind::InComment, '*'), + (FullCodeCharKind::EndComment, '/'), + ], + ); + + check( + "/*\n`````\nc\n```*/", + &[ + (FullCodeCharKind::StartComment, '/'), + (FullCodeCharKind::InComment, '*'), + (FullCodeCharKind::InComment, '\n'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '`'), + (FullCodeCharKind::InComment, '\n'), + (FullCodeCharKind::InCodeBlock, 'c'), + (FullCodeCharKind::InCodeBlock, '\n'), + (FullCodeCharKind::InCodeBlock, '`'), + (FullCodeCharKind::InCodeBlock, '`'), + (FullCodeCharKind::InCodeBlock, '`'), + (FullCodeCharKind::InComment, '*'), + (FullCodeCharKind::EndComment, '/'), + ], + ); + } + #[test] fn comment_code_slices() { let input = "code(); /* test */ 1 + 1"; diff --git a/src/formatting/utils.rs b/src/formatting/utils.rs index c6b579eb4f7..934cd89cb78 100644 --- a/src/formatting/utils.rs +++ b/src/formatting/utils.rs @@ -616,7 +616,8 @@ pub(crate) fn trim_left_preserve_layout( let new_veto_trim_value = (kind == FullCodeCharKind::InString || kind == FullCodeCharKind::InStringCommented) && !line.ends_with('\\'); - let line = if veto_trim || new_veto_trim_value { + let code_block = kind.is_code_block(); + let line = if veto_trim || new_veto_trim_value || code_block { veto_trim = new_veto_trim_value; trimmed = false; line diff --git a/tests/source/issue-4753.rs b/tests/source/issue-4753.rs new file mode 100644 index 00000000000..19772b5b8bc --- /dev/null +++ b/tests/source/issue-4753.rs @@ -0,0 +1,13 @@ +// rustfmt-hard_tabs: true + +/*! +```yaml +foo: + bar: + rustfmt: "don't touch this" +``` +*/ + +fn main() { + println!("Hello, world!"); +} diff --git a/tests/target/issue-4753.rs b/tests/target/issue-4753.rs new file mode 100644 index 00000000000..19772b5b8bc --- /dev/null +++ b/tests/target/issue-4753.rs @@ -0,0 +1,13 @@ +// rustfmt-hard_tabs: true + +/*! +```yaml +foo: + bar: + rustfmt: "don't touch this" +``` +*/ + +fn main() { + println!("Hello, world!"); +}