]> git.lizzy.rs Git - rust.git/blobdiff - src/comment.rs
Cargo fmt and update a test
[rust.git] / src / comment.rs
index d8189df363cbd0597f040f52a0ce1c192bf5cf63..8b8112618b4a8c948ae616846cd74cdc5c2a57b0 100644 (file)
 
 use syntax::codemap::Span;
 
-use {Indent, Shape};
 use config::Config;
 use rewrite::RewriteContext;
-use string::{StringFormat, rewrite_string};
-use utils::wrap_str;
+use shape::{Indent, Shape};
+use string::{rewrite_string, StringFormat};
+use utils::{first_line_width, last_line_width};
 
 fn is_custom_comment(comment: &str) -> bool {
     if !comment.starts_with("//") {
         false
+    } else if let Some(c) = comment.chars().nth(2) {
+        !c.is_alphanumeric() && !c.is_whitespace()
     } else {
-        if let Some(c) = comment.chars().nth(2) {
-            !c.is_alphanumeric() && !c.is_whitespace()
-        } else {
-            false
-        }
+        false
     }
 }
 
-#[derive(PartialEq, Eq)]
+#[derive(Copy, Clone, PartialEq, Eq)]
 pub enum CommentStyle<'a> {
     DoubleSlash,
     TripleSlash,
@@ -66,13 +64,12 @@ 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 => " */",
+            CommentStyle::SingleBullet | CommentStyle::Exclamation => " */",
         }
     }
 
@@ -81,8 +78,7 @@ pub fn line_start(&self) -> &'a str {
             CommentStyle::DoubleSlash => "// ",
             CommentStyle::TripleSlash => "/// ",
             CommentStyle::Doc => "//! ",
-            CommentStyle::SingleBullet |
-            CommentStyle::Exclamation => " * ",
+            CommentStyle::SingleBullet | CommentStyle::Exclamation => " * ",
             CommentStyle::DoubleBullet => " ** ",
             CommentStyle::Custom(opener) => opener,
         }
@@ -94,18 +90,14 @@ pub fn to_str_tuplet(&self) -> (&'a str, &'a str, &'a str) {
 
     pub fn line_with_same_comment_style(&self, line: &str, normalize_comments: bool) -> bool {
         match *self {
-            CommentStyle::DoubleSlash |
-            CommentStyle::TripleSlash |
-            CommentStyle::Doc => {
-                line.trim_left().starts_with(self.line_start().trim_left()) ||
-                    comment_style(line, normalize_comments) == *self
+            CommentStyle::DoubleSlash | CommentStyle::TripleSlash | CommentStyle::Doc => {
+                line.trim_left().starts_with(self.line_start().trim_left())
+                    || comment_style(line, normalize_comments) == *self
             }
-            CommentStyle::DoubleBullet |
-            CommentStyle::SingleBullet |
-            CommentStyle::Exclamation => {
-                line.trim_left().starts_with(self.closer().trim_left()) ||
-                    line.trim_left().starts_with(self.line_start().trim_left()) ||
-                    comment_style(line, normalize_comments) == *self
+            CommentStyle::DoubleBullet | CommentStyle::SingleBullet | CommentStyle::Exclamation => {
+                line.trim_left().starts_with(self.closer().trim_left())
+                    || line.trim_left().starts_with(self.line_start().trim_left())
+                    || comment_style(line, normalize_comments) == *self
             }
             CommentStyle::Custom(opener) => line.trim_left().starts_with(opener.trim_right()),
         }
@@ -129,8 +121,8 @@ fn comment_style(orig: &str, normalize_comments: bool) -> CommentStyle {
         } else {
             CommentStyle::DoubleSlash
         }
-    } else if (orig.starts_with("///") && orig.chars().nth(3).map_or(true, |c| c != '/')) ||
-               (orig.starts_with("/**") && !orig.starts_with("/**/"))
+    } else if (orig.starts_with("///") && orig.chars().nth(3).map_or(true, |c| c != '/'))
+        || (orig.starts_with("/**") && !orig.starts_with("/**/"))
     {
         CommentStyle::TripleSlash
     } else if orig.starts_with("//!") || orig.starts_with("/*!") {
@@ -142,6 +134,81 @@ fn comment_style(orig: &str, normalize_comments: bool) -> CommentStyle {
     }
 }
 
+pub fn combine_strs_with_missing_comments(
+    context: &RewriteContext,
+    prev_str: &str,
+    next_str: &str,
+    span: Span,
+    shape: Shape,
+    allow_extend: bool,
+) -> Option<String> {
+    let mut allow_one_line = !prev_str.contains('\n') && !next_str.contains('\n');
+    let first_sep = if prev_str.is_empty() || next_str.is_empty() {
+        ""
+    } else {
+        " "
+    };
+    let mut one_line_width =
+        last_line_width(prev_str) + first_line_width(next_str) + first_sep.len();
+
+    let indent_str = shape.indent.to_string(context.config);
+    let missing_comment = rewrite_missing_comment(span, shape, context)?;
+
+    if missing_comment.is_empty() {
+        if allow_extend && prev_str.len() + first_sep.len() + next_str.len() <= shape.width {
+            return Some(format!("{}{}{}", prev_str, first_sep, next_str));
+        } else {
+            let sep = if prev_str.is_empty() {
+                String::new()
+            } else {
+                String::from("\n") + &indent_str
+            };
+            return Some(format!("{}{}{}", prev_str, sep, next_str));
+        }
+    }
+
+    // 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 original
+    // layout whenever possible.
+    let original_snippet = context.snippet(span);
+    let prefer_same_line = if let Some(pos) = original_snippet.chars().position(|c| c == '/') {
+        !original_snippet[..pos].contains('\n')
+    } else {
+        !original_snippet.contains('\n')
+    };
+
+    one_line_width -= first_sep.len();
+    let first_sep = if prev_str.is_empty() || missing_comment.is_empty() {
+        String::new()
+    } else {
+        let one_line_width = last_line_width(prev_str) + first_line_width(&missing_comment) + 1;
+        if prefer_same_line && one_line_width <= shape.width {
+            String::from(" ")
+        } else {
+            format!("\n{}", indent_str)
+        }
+    };
+    let second_sep = if missing_comment.is_empty() || next_str.is_empty() {
+        String::new()
+    } else if missing_comment.starts_with("//") {
+        format!("\n{}", indent_str)
+    } else {
+        one_line_width += missing_comment.len() + first_sep.len() + 1;
+        allow_one_line &= !missing_comment.starts_with("//") && !missing_comment.contains('\n');
+        if prefer_same_line && allow_one_line && one_line_width <= shape.width {
+            String::from(" ")
+        } else {
+            format!("\n{}", indent_str)
+        }
+    };
+    Some(format!(
+        "{}{}{}{}{}",
+        prev_str, first_sep, missing_comment, second_sep, next_str,
+    ))
+}
+
 pub fn rewrite_comment(
     orig: &str,
     block_style: bool,
@@ -153,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());
@@ -183,13 +248,7 @@ fn identify_comment(
         .collect::<Vec<_>>()
         .join("\n");
 
-    let first_group_str = try_opt!(rewrite_comment_inner(
-        &first_group,
-        block_style,
-        style,
-        shape,
-        config,
-    ));
+    let first_group_str = rewrite_comment_inner(&first_group, block_style, style, shape, config)?;
     if rest.is_empty() {
         Some(first_group_str)
     } else {
@@ -222,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,
     };
@@ -239,39 +299,91 @@ fn rewrite_comment_inner(
             line = line.trim();
             // Drop old closer.
             if i == line_breaks && line.ends_with("*/") && !line.starts_with("//") {
-                line = &line[..(line.len() - 2)].trim_right();
+                line = line[..(line.len() - 2)].trim_right();
             }
 
             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 config.wrap_comments() && line.len() > max_chars {
-            let rewrite = rewrite_string(line, &fmt).unwrap_or(line.to_owned());
-            result.push_str(&rewrite);
+        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() > 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;
         }
     }
 
@@ -284,6 +396,56 @@ 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(
+    span: Span,
+    shape: Shape,
+    context: &RewriteContext,
+) -> Option<String> {
+    let missing_snippet = context.snippet(span);
+    let trimmed_snippet = missing_snippet.trim();
+    if !trimmed_snippet.is_empty() {
+        rewrite_comment(trimmed_snippet, false, shape, context.config)
+    } else {
+        Some(String::new())
+    }
+}
+
+/// Recover the missing comments in the specified span, if available.
+/// The layout of the comments will be preserved as long as it does not break the code
+/// and its total width does not exceed the max width.
+pub fn recover_missing_comment_in_span(
+    span: Span,
+    shape: Shape,
+    context: &RewriteContext,
+    used_width: usize,
+) -> Option<String> {
+    let missing_comment = rewrite_missing_comment(span, shape, context)?;
+    if missing_comment.is_empty() {
+        Some(String::new())
+    } else {
+        let missing_snippet = context.snippet(span);
+        let pos = missing_snippet.chars().position(|c| c == '/').unwrap_or(0);
+        // 1 = ` `
+        let total_width = missing_comment.len() + used_width + 1;
+        let force_new_line_before_comment =
+            missing_snippet[..pos].contains('\n') || total_width > context.config.max_width();
+        let sep = if force_new_line_before_comment {
+            format!("\n{}", shape.indent.to_string(context.config))
+        } else {
+            String::from(" ")
+        };
+        Some(format!("{}{}", sep, missing_comment))
+    }
+}
+
 /// Trims whitespace and aligns to indent, but otherwise does not change comments.
 fn light_rewrite_comment(orig: &str, offset: Indent, config: &Config) -> Option<String> {
     let lines: Vec<&str> = orig.lines()
@@ -293,7 +455,7 @@ fn light_rewrite_comment(orig: &str, offset: Indent, config: &Config) -> Option<
             // `*` in `/*`.
             let first_non_whitespace = l.find(|c| !char::is_whitespace(c));
             if let Some(fnw) = first_non_whitespace {
-                if l.as_bytes()[fnw] == '*' as u8 && fnw > 0 {
+                if l.as_bytes()[fnw] == b'*' && fnw > 0 {
                     &l[fnw - 1..]
                 } else {
                     &l[fnw..]
@@ -309,24 +471,24 @@ fn light_rewrite_comment(orig: &str, offset: Indent, config: &Config) -> Option<
 /// Trims comment characters and possibly a single space from the left of a string.
 /// Does not trim all whitespace.
 fn left_trim_comment_line<'a>(line: &'a str, style: &CommentStyle) -> &'a str {
-    if line.starts_with("//! ") || line.starts_with("/// ") || line.starts_with("/*! ") ||
-        line.starts_with("/** ")
+    if line.starts_with("//! ") || line.starts_with("/// ") || line.starts_with("/*! ")
+        || line.starts_with("/** ")
     {
         &line[4..]
-    } else if let &CommentStyle::Custom(opener) = style {
+    } else if let CommentStyle::Custom(opener) = *style {
         if line.starts_with(opener) {
             &line[opener.len()..]
         } else {
             &line[opener.trim_right().len()..]
         }
-    } else if line.starts_with("/* ") || line.starts_with("// ") || line.starts_with("//!") ||
-               line.starts_with("///") ||
-               line.starts_with("** ") || line.starts_with("/*!") ||
-               (line.starts_with("/**") && !line.starts_with("/**/"))
+    } else if line.starts_with("/* ") || line.starts_with("// ") || line.starts_with("//!")
+        || line.starts_with("///") || line.starts_with("** ")
+        || line.starts_with("/*!")
+        || (line.starts_with("/**") && !line.starts_with("/**/"))
     {
         &line[3..]
-    } else if line.starts_with("/*") || line.starts_with("* ") || line.starts_with("//") ||
-               line.starts_with("**")
+    } else if line.starts_with("/*") || line.starts_with("* ") || line.starts_with("//")
+        || line.starts_with("**")
     {
         &line[2..]
     } else if line.starts_with('*') {
@@ -348,14 +510,12 @@ fn find_uncommented(&self, pat: &str) -> Option<usize> {
                 None => {
                     return Some(i - pat.len());
                 }
-                Some(c) => {
-                    match kind {
-                        FullCodeCharKind::Normal if b == c => {}
-                        _ => {
-                            needle_iter = pat.chars();
-                        }
+                Some(c) => match kind {
+                    FullCodeCharKind::Normal | FullCodeCharKind::InString if b == c => {}
+                    _ => {
+                        needle_iter = pat.chars();
                     }
-                }
+                },
             }
         }
 
@@ -374,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);
         }
     }
@@ -392,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,
@@ -455,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,
         }
     }
 
@@ -497,47 +688,52 @@ impl<T> Iterator for CharClasses<T>
     type Item = (FullCodeCharKind, T::Item);
 
     fn next(&mut self) -> Option<(FullCodeCharKind, T::Item)> {
-        let item = try_opt!(self.base.next());
+        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,
+            CharClassesStatus::LitString => match chr {
+                '"' => CharClassesStatus::Normal,
+                '\\' => {
+                    char_kind = FullCodeCharKind::InString;
+                    CharClassesStatus::LitStringEscape
                 }
-            }
-            CharClassesStatus::LitStringEscape => CharClassesStatus::LitString,
-            CharClassesStatus::LitChar => {
-                match chr {
-                    '\\' => CharClassesStatus::LitCharEscape,
-                    '\'' => CharClassesStatus::Normal,
-                    _ => CharClassesStatus::LitChar,
+                _ => {
+                    char_kind = FullCodeCharKind::InString;
+                    CharClassesStatus::LitString
                 }
+            },
+            CharClassesStatus::LitStringEscape => {
+                char_kind = FullCodeCharKind::InString;
+                CharClassesStatus::LitString
             }
+            CharClassesStatus::LitChar => match chr {
+                '\\' => CharClassesStatus::LitCharEscape,
+                '\'' => CharClassesStatus::Normal,
+                _ => CharClassesStatus::LitChar,
+            },
             CharClassesStatus::LitCharEscape => CharClassesStatus::LitChar,
-            CharClassesStatus::Normal => {
-                match chr {
-                    '"' => CharClassesStatus::LitString,
-                    '\'' => CharClassesStatus::LitChar,
-                    '/' => {
-                        match self.base.peek() {
-                            Some(next) if next.get_char() == '*' => {
-                                self.status = CharClassesStatus::BlockCommentOpening(1);
-                                return Some((FullCodeCharKind::StartComment, item));
-                            }
-                            Some(next) if next.get_char() == '/' => {
-                                self.status = CharClassesStatus::LineComment;
-                                return Some((FullCodeCharKind::StartComment, item));
-                            }
-                            _ => CharClassesStatus::Normal,
-                        }
+            CharClassesStatus::Normal => match chr {
+                '"' => {
+                    char_kind = FullCodeCharKind::InString;
+                    CharClassesStatus::LitString
+                }
+                '\'' => CharClassesStatus::LitChar,
+                '/' => match self.base.peek() {
+                    Some(next) if next.get_char() == '*' => {
+                        self.status = CharClassesStatus::BlockCommentOpening(1);
+                        return Some((FullCodeCharKind::StartComment, item));
+                    }
+                    Some(next) if next.get_char() == '/' => {
+                        self.status = CharClassesStatus::LineComment;
+                        return Some((FullCodeCharKind::StartComment, item));
                     }
                     _ => CharClassesStatus::Normal,
-                }
-            }
+                },
+                _ => CharClassesStatus::Normal,
+            },
             CharClassesStatus::BlockComment(deepness) => {
-                assert!(deepness != 0);
+                assert_ne!(deepness, 0);
                 self.status = match self.base.peek() {
                     Some(next) if next.get_char() == '/' && chr == '*' => {
                         CharClassesStatus::BlockCommentClosing(deepness - 1)
@@ -564,20 +760,18 @@ fn next(&mut self) -> Option<(FullCodeCharKind, T::Item)> {
                     return Some((FullCodeCharKind::InComment, item));
                 }
             }
-            CharClassesStatus::LineComment => {
-                match chr {
-                    '\n' => {
-                        self.status = CharClassesStatus::Normal;
-                        return Some((FullCodeCharKind::EndComment, item));
-                    }
-                    _ => {
-                        self.status = CharClassesStatus::LineComment;
-                        return Some((FullCodeCharKind::InComment, item));
-                    }
+            CharClassesStatus::LineComment => match chr {
+                '\n' => {
+                    self.status = CharClassesStatus::Normal;
+                    return Some((FullCodeCharKind::EndComment, item));
                 }
-            }
+                _ => {
+                    self.status = CharClassesStatus::LineComment;
+                    return Some((FullCodeCharKind::InComment, item));
+                }
+            },
         };
-        Some((FullCodeCharKind::Normal, item))
+        Some((char_kind, item))
     }
 }
 
@@ -602,11 +796,14 @@ impl<'a> Iterator for UngroupedCommentCodeSlices<'a> {
     type Item = (CodeCharKind, usize, &'a str);
 
     fn next(&mut self) -> Option<Self::Item> {
-        let (kind, (start_idx, _)) = try_opt!(self.iter.next());
+        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();
                 }
             }
@@ -668,9 +865,9 @@ fn next(&mut self) -> Option<Self::Item> {
         let mut iter = CharClasses::new(subslice.char_indices());
 
         for (kind, (i, c)) in &mut iter {
-            let is_comment_connector = self.last_slice_kind == CodeCharKind::Normal &&
-                &subslice[..2] == "//" &&
-                [' ', '\t'].contains(&c);
+            let is_comment_connector = self.last_slice_kind == CodeCharKind::Normal
+                && &subslice[..2] == "//"
+                && [' ', '\t'].contains(&c);
 
             if is_comment_connector && first_whitespace.is_none() {
                 first_whitespace = Some(i);
@@ -720,13 +917,11 @@ pub fn recover_comment_removed(
     new: String,
     span: Span,
     context: &RewriteContext,
-    shape: Shape,
 ) -> Option<String> {
     let snippet = context.snippet(span);
-    if changed_comment_content(&snippet, &new) {
-        // We missed some comments
-        // Keep previous formatting if it satisfies the constrains
-        wrap_str(snippet, context.config.max_width(), shape)
+    if snippet != new && changed_comment_content(&snippet, &new) {
+        // We missed some comments. Keep the original text.
+        Some(snippet)
     } else {
         Some(new)
     }
@@ -785,19 +980,17 @@ impl<'a> Iterator for CommentReducer<'a> {
     type Item = char;
     fn next(&mut self) -> Option<Self::Item> {
         loop {
-            let mut c = try_opt!(self.iter.next());
+            let mut c = self.iter.next()?;
             if self.is_block && self.at_start_line {
                 while c.is_whitespace() {
-                    c = try_opt!(self.iter.next());
+                    c = self.iter.next()?;
                 }
                 // Ignore leading '*'
                 if c == '*' {
-                    c = try_opt!(self.iter.next());
-                }
-            } else {
-                if c == '\n' {
-                    self.at_start_line = true;
+                    c = self.iter.next()?;
                 }
+            } else if c == '\n' {
+                self.at_start_line = true;
             }
             if !c.is_whitespace() {
                 return Some(c);
@@ -812,8 +1005,8 @@ fn remove_comment_header(comment: &str) -> &str {
         &comment[3..]
     } else if comment.starts_with("//") {
         &comment[2..]
-    } else if (comment.starts_with("/**") && !comment.starts_with("/**/")) ||
-               comment.starts_with("/*!")
+    } else if (comment.starts_with("/**") && !comment.starts_with("/**/"))
+        || comment.starts_with("/*!")
     {
         &comment[3..comment.len() - 2]
     } else {
@@ -827,9 +1020,9 @@ fn remove_comment_header(comment: &str) -> &str {
 
 #[cfg(test)]
 mod test {
-    use super::{CharClasses, CodeCharKind, FullCodeCharKind, contains_comment, rewrite_comment,
-                FindUncommented, CommentCodeSlices};
-    use {Indent, Shape};
+    use super::{contains_comment, rewrite_comment, CharClasses, CodeCharKind, CommentCodeSlices,
+                FindUncommented, FullCodeCharKind};
+    use shape::{Indent, Shape};
 
     #[test]
     fn char_classes() {
@@ -933,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()