From efb68ee21a2680794dd1179ed071b245ade9c91e Mon Sep 17 00:00:00 2001 From: Seiichi Uchida Date: Tue, 12 Dec 2017 13:48:24 +0900 Subject: [PATCH] Refactor write_snippet_inner() --- src/missed_spans.rs | 329 ++++++++++++++++++++------------------------ 1 file changed, 151 insertions(+), 178 deletions(-) diff --git a/src/missed_spans.rs b/src/missed_spans.rs index 08fd5bd79e3..4e1f2a37ef4 100644 --- a/src/missed_spans.rs +++ b/src/missed_spans.rs @@ -20,9 +20,28 @@ use shape::{Indent, Shape}; use utils::{count_newlines, last_line_width, mk_sp}; use visitor::FmtVisitor; +struct SnippetStatus { + /// An offset to the current line from the beginnig of the original snippet. + line_start: usize, + /// A length of trailing whitespaces on the current line. + last_wspace: Option, + /// The current line number. + cur_line: usize, +} + +impl SnippetStatus { + fn new(cur_line: usize) -> Self { + SnippetStatus { + line_start: 0, + last_wspace: None, + cur_line, + } + } +} + impl<'a> FmtVisitor<'a> { fn output_at_start(&self) -> bool { - self.buffer.len() == 0 + self.buffer.is_empty() } // TODO these format_missing methods are ugly. Refactor and add unit tests @@ -77,8 +96,8 @@ impl<'a> FmtVisitor<'a> { let snippet = self.snippet(span); if snippet.trim().is_empty() && !out_of_file_lines_range!(self, span) { // Keep vertical spaces within range. - self.push_vertical_spaces(count_newlines(&snippet)); - process_last_snippet(self, "", &snippet); + self.push_vertical_spaces(count_newlines(snippet)); + process_last_snippet(self, "", snippet); } else { self.write_snippet(span, &process_last_snippet); } @@ -86,11 +105,7 @@ impl<'a> FmtVisitor<'a> { fn push_vertical_spaces(&mut self, mut newline_count: usize) { // The buffer already has a trailing newline. - let offset = if last_line_width(&self.buffer) == 0 { - 0 - } else { - 1 - }; + let offset = if self.buffer.ends_with('\n') { 0 } else { 1 }; let newline_upper_bound = self.config.blank_lines_upper_bound() + offset; let newline_lower_bound = self.config.blank_lines_lower_bound() + offset; if newline_count > newline_upper_bound { @@ -121,86 +136,7 @@ impl<'a> FmtVisitor<'a> { debug!("write_snippet `{}`", snippet); - self.write_snippet_inner(big_snippet, big_diff, &snippet, span, process_last_snippet); - } - - fn process_comment( - &mut self, - status: &mut SnippetStatus, - snippet: &str, - big_snippet: &str, - offset: usize, - big_diff: usize, - subslice: &str, - file_name: &str, - ) -> bool { - let last_char = big_snippet[..(offset + big_diff)] - .chars() - .rev() - .skip_while(|rev_c| [' ', '\t'].contains(rev_c)) - .next(); - - let fix_indent = last_char.map_or(true, |rev_c| ['{', '\n'].contains(&rev_c)); - - let subslice_num_lines = count_newlines(subslice); - let skip_this_range = !self.config.file_lines().intersects_range( - file_name, - status.cur_line, - status.cur_line + subslice_num_lines, - ); - - if status.rewrite_next_comment && skip_this_range { - status.rewrite_next_comment = false; - } - - if status.rewrite_next_comment { - let comment_indent = if fix_indent { - if let Some('{') = last_char { - self.push_str("\n"); - } - let indent_str = self.block_indent.to_string(self.config); - self.push_str(&indent_str); - self.block_indent - } else { - self.push_str(" "); - Indent::from_width(self.config, last_line_width(&self.buffer)) - }; - let comment_width = ::std::cmp::min( - self.config.comment_width(), - self.config.max_width() - self.block_indent.width(), - ); - let comment_shape = Shape::legacy(comment_width, comment_indent); - let comment_str = rewrite_comment(subslice, false, comment_shape, self.config) - .unwrap_or_else(|| String::from(subslice)); - self.push_str(&comment_str); - - status.last_wspace = None; - status.line_start = offset + subslice.len(); - - if let Some('/') = subslice.chars().nth(1) { - // check that there are no contained block comments - if !subslice - .split('\n') - .map(|s| s.trim_left()) - .any(|s| s.len() >= 2 && &s[0..2] == "/*") - { - // Add a newline after line comments - self.push_str("\n"); - } - } else if status.line_start <= snippet.len() { - // For other comments add a newline if there isn't one at the end already - match snippet[status.line_start..].chars().next() { - Some('\n') | Some('\r') => (), - _ => self.push_str("\n"), - } - } - - status.cur_line += subslice_num_lines; - true - } else { - status.rewrite_next_comment = false; - false - } + self.write_snippet_inner(big_snippet, big_diff, snippet, span, process_last_snippet); } fn write_snippet_inner( @@ -220,19 +156,6 @@ impl<'a> FmtVisitor<'a> { let file_name = &char_pos.file.name; let mut status = SnippetStatus::new(char_pos.line); - fn replace_chars<'a>(string: &'a str) -> Cow<'a, str> { - if string.contains(char::is_whitespace) { - Cow::from( - string - .chars() - .map(|ch| if ch.is_whitespace() { ch } else { 'X' }) - .collect::(), - ) - } else { - Cow::from(string) - } - } - let snippet = &*match self.config.write_mode() { WriteMode::Coverage => replace_chars(old_snippet), _ => Cow::from(old_snippet), @@ -241,98 +164,148 @@ impl<'a> FmtVisitor<'a> { for (kind, offset, subslice) in CommentCodeSlices::new(snippet) { debug!("{:?}: {:?}", kind, subslice); - if let CodeCharKind::Comment = kind { - if self.process_comment( + let newline_count = count_newlines(subslice); + let within_file_lines_range = self.config.file_lines().intersects_range( + file_name, + status.cur_line, + status.cur_line + newline_count, + ); + + if CodeCharKind::Comment == kind && within_file_lines_range { + // 1: comment. + self.process_comment( &mut status, snippet, - big_snippet, + &big_snippet[..(offset + big_diff)], offset, - big_diff, subslice, - file_name, - ) { - continue; - } - } - - let newline_count = count_newlines(&subslice); - if subslice.trim().is_empty() && newline_count > 0 - && self.config.file_lines().intersects_range( - file_name, - status.cur_line, - status.cur_line + newline_count, - ) { + ); + } else if subslice.trim().is_empty() && newline_count > 0 && within_file_lines_range { + // 2: blank lines. self.push_vertical_spaces(newline_count); status.cur_line += newline_count; - status.rewrite_next_comment = true; status.line_start = offset + newline_count; } else { - for (mut i, c) in subslice.char_indices() { - i += offset; - - if c == '\n' { - if !self.config - .file_lines() - .contains_line(file_name, status.cur_line) - { - status.last_wspace = None; - } - - if let Some(lw) = status.last_wspace { - self.push_str(&snippet[status.line_start..lw]); - self.push_str("\n"); - } else { - self.push_str(&snippet[status.line_start..i + 1]); - } - - status.cur_line += 1; - status.line_start = i + 1; - status.last_wspace = None; - status.rewrite_next_comment = true; - } else if c.is_whitespace() { - if status.last_wspace.is_none() { - status.last_wspace = Some(i); - } - } else if c == ';' { - if status.last_wspace.is_some() { - status.line_start = i; - } - - status.rewrite_next_comment = true; - status.last_wspace = None; - } else { - status.rewrite_next_comment = true; - status.last_wspace = None; - } - } - - let remaining = snippet[status.line_start..subslice.len() + offset].trim(); - if !remaining.is_empty() { - self.push_str(remaining); - status.line_start = subslice.len() + offset; - status.rewrite_next_comment = true; - } + // 3: code which we failed to format or which is not within file-lines range. + self.process_missing_code(&mut status, snippet, subslice, offset, file_name); } } process_last_snippet(self, &snippet[status.line_start..], snippet); } -} -struct SnippetStatus { - line_start: usize, - last_wspace: Option, - rewrite_next_comment: bool, - cur_line: usize, -} + fn process_comment( + &mut self, + status: &mut SnippetStatus, + snippet: &str, + big_snippet: &str, + offset: usize, + subslice: &str, + ) { + let last_char = big_snippet + .chars() + .rev() + .skip_while(|rev_c| [' ', '\t'].contains(rev_c)) + .next(); -impl SnippetStatus { - fn new(cur_line: usize) -> Self { - SnippetStatus { - line_start: 0, - last_wspace: None, - rewrite_next_comment: true, - cur_line, + let fix_indent = last_char.map_or(true, |rev_c| ['{', '\n'].contains(&rev_c)); + + if fix_indent { + if let Some('{') = last_char { + self.push_str("\n"); + } + let indent_str = self.block_indent.to_string(self.config); + self.push_str(&indent_str); + } else { + self.push_str(" "); + } + + let comment_width = ::std::cmp::min( + self.config.comment_width(), + self.config.max_width() - self.block_indent.width(), + ); + let comment_indent = Indent::from_width(self.config, last_line_width(&self.buffer)); + let comment_shape = Shape::legacy(comment_width, comment_indent); + let comment_str = rewrite_comment(subslice, false, comment_shape, self.config) + .unwrap_or_else(|| String::from(subslice)); + self.push_str(&comment_str); + + status.last_wspace = None; + status.line_start = offset + subslice.len(); + + if let Some('/') = subslice.chars().nth(1) { + // check that there are no contained block comments + if !subslice + .split('\n') + .map(|s| s.trim_left()) + .any(|s| s.len() >= 2 && &s[0..2] == "/*") + { + // Add a newline after line comments + self.push_str("\n"); + } + } else if status.line_start <= snippet.len() { + // For other comments add a newline if there isn't one at the end already + match snippet[status.line_start..].chars().next() { + Some('\n') | Some('\r') => (), + _ => self.push_str("\n"), + } + } + + status.cur_line += count_newlines(subslice); + } + + fn process_missing_code( + &mut self, + status: &mut SnippetStatus, + snippet: &str, + subslice: &str, + offset: usize, + file_name: &str, + ) { + for (mut i, c) in subslice.char_indices() { + i += offset; + + if c == '\n' { + let skip_this_line = !self.config + .file_lines() + .contains_line(file_name, status.cur_line); + if skip_this_line { + status.last_wspace = None; + } + + if let Some(lw) = status.last_wspace { + self.push_str(&snippet[status.line_start..lw]); + self.push_str("\n"); + status.last_wspace = None; + } else { + self.push_str(&snippet[status.line_start..i + 1]); + } + + status.cur_line += 1; + status.line_start = i + 1; + } else if c.is_whitespace() && status.last_wspace.is_none() { + status.last_wspace = Some(i); + } else if c == ';' && status.last_wspace.is_some() { + status.line_start = i; + status.last_wspace = None; + } else { + status.last_wspace = None; + } + } + + let remaining = snippet[status.line_start..subslice.len() + offset].trim(); + if !remaining.is_empty() { + self.push_str(remaining); + status.line_start = subslice.len() + offset; } } } + +fn replace_chars(string: &str) -> Cow { + Cow::from( + string + .chars() + .map(|ch| if ch.is_whitespace() { ch } else { 'X' }) + .collect::(), + ) +}