diff --git a/src/lib.rs b/src/lib.rs index 432e61e874b..97f60329421 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -231,6 +231,16 @@ pub fn new(block_indent: usize, alignment: usize) -> Indent { } } + pub fn from_width(config: &Config, width: usize) -> Indent { + if config.hard_tabs() { + let tab_num = width / config.tab_spaces(); + let alignment = width % config.tab_spaces(); + Indent::new(config.tab_spaces() * tab_num, alignment) + } else { + Indent::new(width, 0) + } + } + pub fn empty() -> Indent { Indent::new(0, 0) } diff --git a/src/macros.rs b/src/macros.rs index b1f740e6a87..12965b61560 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -19,8 +19,6 @@ // List-like invocations with parentheses will be formatted as function calls, // and those with brackets will be formatted as array literals. -use std::iter::repeat; - use syntax::ast; use syntax::codemap::BytePos; use syntax::parse::new_parser_from_tts; @@ -118,14 +116,18 @@ pub fn rewrite_macro( Ok(expr) => { // Recovered errors. if context.parse_session.span_diagnostic.has_errors() { - return indent_macro_snippet(&context.snippet(mac.span), shape.indent); + return indent_macro_snippet( + context, + &context.snippet(mac.span), + shape.indent, + ); } expr } Err(mut e) => { e.cancel(); - return indent_macro_snippet(&context.snippet(mac.span), shape.indent); + return indent_macro_snippet(context, &context.snippet(mac.span), shape.indent); } }; @@ -244,7 +246,7 @@ pub fn rewrite_macro( } MacroStyle::Braces => { // Skip macro invocations with braces, for now. - indent_macro_snippet(&context.snippet(mac.span), shape.indent) + indent_macro_snippet(context, &context.snippet(mac.span), shape.indent) } } } @@ -308,32 +310,62 @@ fn macro_style(mac: &ast::Mac, context: &RewriteContext) -> MacroStyle { // ), /// } /// ``` -fn indent_macro_snippet(macro_str: &str, indent: Indent) -> Option { - let min_prefix_space_width = - try_opt!(macro_str.lines().skip(1).map(get_prefix_space_width).min()); - +fn indent_macro_snippet( + context: &RewriteContext, + macro_str: &str, + indent: Indent, +) -> Option { let mut lines = macro_str.lines(); - let first_line = try_opt!(lines.next()); + let first_line = try_opt!(lines.next().map(|s| s.trim_right())); + let mut trimmed_lines = Vec::with_capacity(16); + + let min_prefix_space_width = try_opt!( + lines + .filter_map(|line| { + let prefix_space_width = if is_empty_line(line) { + None + } else { + Some(get_prefix_space_width(context, line)) + }; + trimmed_lines.push((line.trim(), prefix_space_width)); + prefix_space_width + }) + .min() + ); Some( String::from(first_line) + "\n" + - &lines - .map(|line| { - let new_indent_width = indent.width() + - get_prefix_space_width(line) - .checked_sub(min_prefix_space_width) - .unwrap_or(0); - repeat_white_space(new_indent_width) + line.trim() + &trimmed_lines + .iter() + .map(|&(line, prefix_space_width)| match prefix_space_width { + Some(original_indent_width) => { + let new_indent_width = indent.width() + + original_indent_width + .checked_sub(min_prefix_space_width) + .unwrap_or(0); + let new_indent = Indent::from_width(context.config, new_indent_width); + new_indent.to_string(context.config) + line.trim() + } + None => String::new(), }) .collect::>() .join("\n"), ) } -fn get_prefix_space_width(s: &str) -> usize { - s.chars().position(|c| c != ' ').unwrap_or(0) +fn get_prefix_space_width(context: &RewriteContext, s: &str) -> usize { + let mut width = 0; + let mut iter = s.chars(); + while let Some(c) = iter.next() { + match c { + ' ' => width += 1, + '\t' => width += context.config.tab_spaces(), + _ => return width, + } + } + width } -fn repeat_white_space(ws_count: usize) -> String { - repeat(" ").take(ws_count).collect::() +fn is_empty_line(s: &str) -> bool { + s.is_empty() || s.chars().all(char::is_whitespace) } diff --git a/tests/source/macros.rs b/tests/source/macros.rs index d8e6efe9199..caccd75a700 100644 --- a/tests/source/macros.rs +++ b/tests/source/macros.rs @@ -134,6 +134,36 @@ fn issue_1885() { }).collect::>(); } +fn issue_1917() { + mod x { + quickcheck! { + fn test(a: String, s: String, b: String) -> TestResult { + if a.find(&s).is_none() { + + TestResult::from_bool(true) + } else { + TestResult::discard() + } + } + } + } +} + +fn issue_1921() { + // Macro with tabs. + lazy_static! { + static ref ONE: u32 = 1; + static ref TWO: u32 = 2; + static ref THREE: u32 = 3; + static ref FOUR: u32 = { + let mut acc = 1; + acc += 1; + acc += 2; + acc + } +} +} + // Put the following tests with macro invocations whose arguments cannot be parsed as expressioins // at the end of the file for now. diff --git a/tests/target/macros.rs b/tests/target/macros.rs index 21c0b983ff9..0b651cc5445 100644 --- a/tests/target/macros.rs +++ b/tests/target/macros.rs @@ -178,6 +178,36 @@ fn issue_1885() { .collect::>(); } +fn issue_1917() { + mod x { + quickcheck! { + fn test(a: String, s: String, b: String) -> TestResult { + if a.find(&s).is_none() { + + TestResult::from_bool(true) + } else { + TestResult::discard() + } + } + } + } +} + +fn issue_1921() { + // Macro with tabs. + lazy_static! { + static ref ONE: u32 = 1; + static ref TWO: u32 = 2; + static ref THREE: u32 = 3; + static ref FOUR: u32 = { + let mut acc = 1; + acc += 1; + acc += 2; + acc + } + } +} + // Put the following tests with macro invocations whose arguments cannot be parsed as expressioins // at the end of the file for now.