]> git.lizzy.rs Git - rust.git/blobdiff - src/comment.rs
Cargo fmt and update a test
[rust.git] / src / comment.rs
index f966d658dc8a092bffe0f112979876843ad669b8..8b8112618b4a8c948ae616846cd74cdc5c2a57b0 100644 (file)
@@ -64,10 +64,10 @@ pub fn opener(&self) -> &'a str {
 
     pub fn closer(&self) -> &'a str {
         match *self {
-            CommentStyle::DoubleSlash |
-            CommentStyle::TripleSlash |
-            CommentStyle::Custom(..) |
-            CommentStyle::Doc => "",
+            CommentStyle::DoubleSlash
+            | CommentStyle::TripleSlash
+            | CommentStyle::Custom(..)
+            CommentStyle::Doc => "",
             CommentStyle::DoubleBullet => " **/",
             CommentStyle::SingleBullet | CommentStyle::Exclamation => " */",
         }
@@ -170,7 +170,7 @@ pub fn combine_strs_with_missing_comments(
     // We have a missing comment between the first expression and the second expression.
 
     // Peek the the original source code and find out whether there is a newline between the first
-    // expression and the second expression or the missing comment. We will preserve the orginal
+    // expression and the second expression or the missing comment. We will preserve the original
     // layout whenever possible.
     let original_snippet = context.snippet(span);
     let prefer_same_line = if let Some(pos) = original_snippet.chars().position(|c| c == '/') {
@@ -205,11 +205,7 @@ pub fn combine_strs_with_missing_comments(
     };
     Some(format!(
         "{}{}{}{}{}",
-        prev_str,
-        first_sep,
-        missing_comment,
-        second_sep,
-        next_str,
+        prev_str, first_sep, missing_comment, second_sep, next_str,
     ))
 }
 
@@ -224,9 +220,7 @@ pub fn rewrite_comment(
     // we should stop now.
     let num_bare_lines = orig.lines()
         .map(|line| line.trim())
-        .filter(|l| {
-            !(l.starts_with('*') || l.starts_with("//") || l.starts_with("/*"))
-        })
+        .filter(|l| !(l.starts_with('*') || l.starts_with("//") || l.starts_with("/*")))
         .count();
     if num_bare_lines > 0 && !config.normalize_comments() {
         return Some(orig.to_owned());
@@ -287,12 +281,13 @@ fn rewrite_comment_inner(
         .checked_sub(closer.len() + opener.len())
         .unwrap_or(1);
     let indent_str = shape.indent.to_string(config);
-    let fmt = StringFormat {
+    let fmt_indent = shape.indent + (opener.len() - line_start.len());
+    let mut fmt = StringFormat {
         opener: "",
         closer: "",
         line_start: line_start,
         line_end: "",
-        shape: Shape::legacy(max_chars, shape.indent + (opener.len() - line_start.len())),
+        shape: Shape::legacy(max_chars, fmt_indent),
         trim_end: true,
         config: config,
     };
@@ -310,33 +305,85 @@ fn rewrite_comment_inner(
             line
         })
         .map(|s| left_trim_comment_line(s, &style))
-        .map(|line| if orig.starts_with("/*") && line_breaks == 0 {
-            line.trim_left()
-        } else {
-            line
+        .map(|line| {
+            if orig.starts_with("/*") && line_breaks == 0 {
+                line.trim_left()
+            } else {
+                line
+            }
         });
 
     let mut result = opener.to_owned();
+    let mut is_prev_line_multi_line = false;
+    let mut inside_code_block = false;
+    let comment_line_separator = format!("\n{}{}", indent_str, line_start);
     for line in lines {
         if result == opener {
             if line.is_empty() {
                 continue;
             }
+        } else if is_prev_line_multi_line && !line.is_empty() {
+            result.push(' ')
         } else {
-            result.push('\n');
-            result.push_str(&indent_str);
-            result.push_str(line_start);
+            result.push_str(&comment_line_separator);
+        }
+
+        if line.starts_with("```") {
+            inside_code_block = !inside_code_block;
+        }
+        if inside_code_block {
+            result.push_str(line);
+            continue;
         }
 
-        if config.wrap_comments() && line.len() > max_chars {
-            let rewrite = rewrite_string(line, &fmt).unwrap_or_else(|| line.to_owned());
-            result.push_str(&rewrite);
+        if config.wrap_comments() && line.len() > fmt.shape.width && !has_url(line) {
+            match rewrite_string(line, &fmt, Some(max_chars)) {
+                Some(ref s) => {
+                    is_prev_line_multi_line = s.contains('\n');
+                    result.push_str(s);
+                }
+                None if is_prev_line_multi_line => {
+                    // We failed to put the current `line` next to the previous `line`.
+                    // Remove the trailing space, then start rewrite on the next line.
+                    result.pop();
+                    result.push_str(&comment_line_separator);
+                    fmt.shape = Shape::legacy(max_chars, fmt_indent);
+                    match rewrite_string(line, &fmt, Some(max_chars)) {
+                        Some(ref s) => {
+                            is_prev_line_multi_line = s.contains('\n');
+                            result.push_str(s);
+                        }
+                        None => {
+                            is_prev_line_multi_line = false;
+                            result.push_str(line);
+                        }
+                    }
+                }
+                None => {
+                    is_prev_line_multi_line = false;
+                    result.push_str(line);
+                }
+            }
+
+            fmt.shape = if is_prev_line_multi_line {
+                // 1 = " "
+                let offset = 1 + last_line_width(&result) - line_start.len();
+                Shape {
+                    width: max_chars.checked_sub(offset).unwrap_or(0),
+                    indent: fmt_indent,
+                    offset: fmt.shape.offset + offset,
+                }
+            } else {
+                Shape::legacy(max_chars, fmt_indent)
+            };
         } else {
             if line.is_empty() && result.ends_with(' ') {
                 // Remove space if this is an empty comment or a doc comment.
                 result.pop();
             }
             result.push_str(line);
+            fmt.shape = Shape::legacy(max_chars, fmt_indent);
+            is_prev_line_multi_line = false;
         }
     }
 
@@ -349,6 +396,12 @@ fn rewrite_comment_inner(
     Some(result)
 }
 
+/// Returns true if the given string MAY include URLs or alike.
+fn has_url(s: &str) -> bool {
+    // This function may return false positive, but should get its job done in most cases.
+    s.contains("https://") || s.contains("http://") || s.contains("ftp://") || s.contains("file://")
+}
+
 /// Given the span, rewrite the missing comment inside it if available.
 /// Note that the given span must only include comments (or leading/trailing whitespaces).
 pub fn rewrite_missing_comment(
@@ -458,7 +511,7 @@ fn find_uncommented(&self, pat: &str) -> Option<usize> {
                     return Some(i - pat.len());
                 }
                 Some(c) => match kind {
-                    FullCodeCharKind::Normal if b == c => {}
+                    FullCodeCharKind::Normal | FullCodeCharKind::InString if b == c => {}
                     _ => {
                         needle_iter = pat.chars();
                     }
@@ -481,7 +534,7 @@ fn find_uncommented(&self, pat: &str) -> Option<usize> {
 pub fn find_comment_end(s: &str) -> Option<usize> {
     let mut iter = CharClasses::new(s.char_indices());
     for (kind, (i, _c)) in &mut iter {
-        if kind == FullCodeCharKind::Normal {
+        if kind == FullCodeCharKind::Normal || kind == FullCodeCharKind::InString {
             return Some(i);
         }
     }
@@ -499,6 +552,35 @@ pub fn contains_comment(text: &str) -> bool {
     CharClasses::new(text.chars()).any(|(kind, _)| kind.is_comment())
 }
 
+/// Remove trailing spaces from the specified snippet. We do not remove spaces
+/// inside strings or comments.
+pub fn remove_trailing_white_spaces(text: &str) -> String {
+    let mut buffer = String::with_capacity(text.len());
+    let mut space_buffer = String::with_capacity(128);
+    for (char_kind, c) in CharClasses::new(text.chars()) {
+        match c {
+            '\n' => {
+                if char_kind == FullCodeCharKind::InString {
+                    buffer.push_str(&space_buffer);
+                }
+                space_buffer.clear();
+                buffer.push('\n');
+            }
+            _ if c.is_whitespace() => {
+                space_buffer.push(c);
+            }
+            _ => {
+                if !space_buffer.is_empty() {
+                    buffer.push_str(&space_buffer);
+                    space_buffer.clear();
+                }
+                buffer.push(c);
+            }
+        }
+    }
+    buffer
+}
+
 struct CharClasses<T>
 where
     T: Iterator,
@@ -562,15 +644,17 @@ enum FullCodeCharKind {
     InComment,
     /// Last character of a comment, '\n' for a line comment, '/' for a block comment.
     EndComment,
+    /// Inside a string.
+    InString,
 }
 
 impl FullCodeCharKind {
     fn is_comment(&self) -> bool {
         match *self {
-            FullCodeCharKind::Normal => false,
-            FullCodeCharKind::StartComment |
-            FullCodeCharKind::InComment |
-            FullCodeCharKind::EndComment => true,
+            FullCodeCharKind::StartComment
+            | FullCodeCharKind::InComment
+            | FullCodeCharKind::EndComment => true,
+            _ => false,
         }
     }
 
@@ -606,13 +690,23 @@ impl<T> Iterator for CharClasses<T>
     fn next(&mut self) -> Option<(FullCodeCharKind, T::Item)> {
         let item = self.base.next()?;
         let chr = item.get_char();
+        let mut char_kind = FullCodeCharKind::Normal;
         self.status = match self.status {
             CharClassesStatus::LitString => match chr {
                 '"' => CharClassesStatus::Normal,
-                '\\' => CharClassesStatus::LitStringEscape,
-                _ => CharClassesStatus::LitString,
+                '\\' => {
+                    char_kind = FullCodeCharKind::InString;
+                    CharClassesStatus::LitStringEscape
+                }
+                _ => {
+                    char_kind = FullCodeCharKind::InString;
+                    CharClassesStatus::LitString
+                }
             },
-            CharClassesStatus::LitStringEscape => CharClassesStatus::LitString,
+            CharClassesStatus::LitStringEscape => {
+                char_kind = FullCodeCharKind::InString;
+                CharClassesStatus::LitString
+            }
             CharClassesStatus::LitChar => match chr {
                 '\\' => CharClassesStatus::LitCharEscape,
                 '\'' => CharClassesStatus::Normal,
@@ -620,7 +714,10 @@ fn next(&mut self) -> Option<(FullCodeCharKind, T::Item)> {
             },
             CharClassesStatus::LitCharEscape => CharClassesStatus::LitChar,
             CharClassesStatus::Normal => match chr {
-                '"' => CharClassesStatus::LitString,
+                '"' => {
+                    char_kind = FullCodeCharKind::InString;
+                    CharClassesStatus::LitString
+                }
                 '\'' => CharClassesStatus::LitChar,
                 '/' => match self.base.peek() {
                     Some(next) if next.get_char() == '*' => {
@@ -674,7 +771,7 @@ fn next(&mut self) -> Option<(FullCodeCharKind, T::Item)> {
                 }
             },
         };
-        Some((FullCodeCharKind::Normal, item))
+        Some((char_kind, item))
     }
 }
 
@@ -701,9 +798,12 @@ impl<'a> Iterator for UngroupedCommentCodeSlices<'a> {
     fn next(&mut self) -> Option<Self::Item> {
         let (kind, (start_idx, _)) = self.iter.next()?;
         match kind {
-            FullCodeCharKind::Normal => {
+            FullCodeCharKind::Normal | FullCodeCharKind::InString => {
                 // Consume all the Normal code
-                while let Some(&(FullCodeCharKind::Normal, (_, _))) = self.iter.peek() {
+                while let Some(&(char_kind, _)) = self.iter.peek() {
+                    if char_kind.is_comment() {
+                        break;
+                    }
                     let _ = self.iter.next();
                 }
             }
@@ -1026,7 +1126,7 @@ fn format_comments() {
     fn uncommented(text: &str) -> String {
         CharClasses::new(text.chars())
             .filter_map(|(s, c)| match s {
-                FullCodeCharKind::Normal => Some(c),
+                FullCodeCharKind::Normal | FullCodeCharKind::InString => Some(c),
                 _ => None,
             })
             .collect()