Merge pull request #216 from marcusklaas/format-fields

WIP: Format expression chains
This commit is contained in:
Marcus Klaas de Vries 2015-09-11 00:56:14 +02:00
commit 0b7b3c8725
30 changed files with 735 additions and 355 deletions

View File

@ -82,7 +82,9 @@ fn main() {
}
fn print_usage<S: Into<String>>(reason: S) {
println!("{}\n\r usage: rustfmt [-h Help] [--write-mode=[replace|overwrite|display|diff]] <file_name>", reason.into());
println!("{}\n\r usage: rustfmt [-h Help] [--write-mode=[replace|overwrite|display|diff]] \
<file_name>",
reason.into());
}
fn determine_params<I>(args: I) -> Option<(Vec<String>, WriteMode)>
@ -123,6 +125,5 @@ fn determine_params<I>(args: I) -> Option<(Vec<String>, WriteMode)>
return None;
}
Some((rustc_args, write_mode))
}

195
src/chains.rs Normal file
View File

@ -0,0 +1,195 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Formatting of chained expressions, i.e. expressions which are chained by
// dots: struct and enum field access and method calls.
//
// Instead of walking these subexpressions one-by-one, as is our usual strategy
// for expression formatting, we collect maximal sequences of these expressions
// and handle them simultaneously.
//
// Whenever possible, the entire chain is put on a single line. If that fails,
// we put each subexpression on a separate, much like the (default) function
// argument function argument strategy.
use rewrite::{Rewrite, RewriteContext};
use utils::{first_line_width, make_indent};
use expr::rewrite_call;
use syntax::{ast, ptr};
use syntax::codemap::{mk_sp, Span};
use syntax::print::pprust;
pub fn rewrite_chain(mut expr: &ast::Expr,
context: &RewriteContext,
width: usize,
offset: usize)
-> Option<String> {
let total_span = expr.span;
let mut subexpr_list = vec![expr];
while let Some(subexpr) = pop_expr_chain(expr) {
subexpr_list.push(subexpr);
expr = subexpr;
}
let parent = subexpr_list.pop().unwrap();
let parent_rewrite = try_opt!(expr.rewrite(context, width, offset));
let (extra_indent, extend) = if !parent_rewrite.contains('\n') && is_continuable(parent) ||
parent_rewrite.len() <= context.config.tab_spaces {
(parent_rewrite.len(), true)
} else {
(context.config.tab_spaces, false)
};
let indent = offset + extra_indent;
let max_width = try_opt!(width.checked_sub(extra_indent));
let mut rewrites = try_opt!(subexpr_list.iter()
.rev()
.map(|e| {
rewrite_chain_expr(e,
total_span,
context,
max_width,
indent)
})
.collect::<Option<Vec<_>>>());
// Total of all items excluding the last.
let almost_total = rewrites.split_last()
.unwrap()
.1
.iter()
.fold(0, |a, b| a + first_line_width(b)) +
parent_rewrite.len();
let total_width = almost_total + first_line_width(rewrites.last().unwrap());
let veto_single_line = if context.config.take_source_hints && subexpr_list.len() > 1 {
// Look at the source code. Unless all chain elements start on the same
// line, we won't consider putting them on a single line either.
let first_line_no = context.codemap.lookup_char_pos(subexpr_list[0].span.lo).line;
subexpr_list[1..]
.iter()
.any(|ex| context.codemap.lookup_char_pos(ex.span.hi).line != first_line_no)
} else {
false
};
let fits_single_line = !veto_single_line &&
match subexpr_list[0].node {
ast::Expr_::ExprMethodCall(ref method_name, ref types, ref expressions)
if context.config.chains_overflow_last => {
let (last, init) = rewrites.split_last_mut().unwrap();
if init.iter().all(|s| !s.contains('\n')) && total_width <= width {
let last_rewrite = width.checked_sub(almost_total)
.and_then(|inner_width| {
rewrite_method_call(method_name.node,
types,
expressions,
total_span,
context,
inner_width,
offset + almost_total)
});
match last_rewrite {
Some(mut string) => {
::std::mem::swap(&mut string, last);
true
}
None => false,
}
} else {
false
}
}
_ => total_width <= width && rewrites.iter().all(|s| !s.contains('\n')),
};
let connector = if fits_single_line {
String::new()
} else {
format!("\n{}", make_indent(indent))
};
let first_connector = if extend {
""
} else {
&connector[..]
};
Some(format!("{}{}{}", parent_rewrite, first_connector, rewrites.join(&connector)))
}
fn pop_expr_chain<'a>(expr: &'a ast::Expr) -> Option<&'a ast::Expr> {
match expr.node {
ast::Expr_::ExprMethodCall(_, _, ref expressions) => {
Some(&expressions[0])
}
ast::Expr_::ExprTupField(ref subexpr, _) |
ast::Expr_::ExprField(ref subexpr, _) => {
Some(subexpr)
}
_ => None,
}
}
fn rewrite_chain_expr(expr: &ast::Expr,
span: Span,
context: &RewriteContext,
width: usize,
offset: usize)
-> Option<String> {
match expr.node {
ast::Expr_::ExprMethodCall(ref method_name, ref types, ref expressions) => {
let inner = &RewriteContext {
block_indent: offset,
..*context
};
rewrite_method_call(method_name.node, types, expressions, span, inner, width, offset)
}
ast::Expr_::ExprField(_, ref field) => {
Some(format!(".{}", field.node))
}
ast::Expr_::ExprTupField(_, ref field) => {
Some(format!(".{}", field.node))
}
_ => unreachable!(),
}
}
// Determines we can continue formatting a given expression on the same line.
fn is_continuable(expr: &ast::Expr) -> bool {
match expr.node {
ast::Expr_::ExprPath(..) => true,
_ => false,
}
}
fn rewrite_method_call(method_name: ast::Ident,
types: &[ptr::P<ast::Ty>],
args: &[ptr::P<ast::Expr>],
span: Span,
context: &RewriteContext,
width: usize,
offset: usize)
-> Option<String> {
let type_str = if types.is_empty() {
String::new()
} else {
let type_list = types.iter().map(|ty| pprust::ty_to_string(ty)).collect::<Vec<_>>();
format!("::<{}>", type_list.join(", "))
};
let callee_str = format!(".{}{}", method_name, type_str);
let span = mk_sp(args[0].span.hi, span.hi);
rewrite_call(context, &callee_str, &args[1..], span, width, offset)
}

View File

@ -25,8 +25,7 @@ pub fn rewrite_comment(orig: &str, block_style: bool, width: usize, offset: usiz
("// ", "", "// ")
};
let max_chars = width.checked_sub(closer.len()).unwrap_or(1)
.checked_sub(opener.len()).unwrap_or(1);
let max_chars = width.checked_sub(closer.len() + opener.len()).unwrap_or(1);
let fmt = StringFormat {
opener: "",
@ -41,44 +40,45 @@ pub fn rewrite_comment(orig: &str, block_style: bool, width: usize, offset: usiz
let indent_str = make_indent(offset);
let line_breaks = s.chars().filter(|&c| c == '\n').count();
let (_, mut s) = s.lines().enumerate()
.map(|(i, mut line)| {
line = line.trim();
let (_, mut s) = s.lines()
.enumerate()
.map(|(i, mut line)| {
line = line.trim();
// Drop old closer.
if i == line_breaks && line.ends_with("*/") && !line.starts_with("//") {
line = &line[..(line.len() - 2)];
}
// Drop old closer.
if i == line_breaks && line.ends_with("*/") && !line.starts_with("//") {
line = &line[..(line.len() - 2)];
}
line.trim_right()
})
.map(left_trim_comment_line)
.map(|line| {
if line_breaks == 0 {
line.trim_left()
} else {
line
}
})
.fold((true, opener.to_owned()),
|(first, mut acc), line| {
if !first {
acc.push('\n');
acc.push_str(&indent_str);
acc.push_str(line_start);
}
line.trim_right()
})
.map(left_trim_comment_line)
.map(|line| {
if line_breaks == 0 {
line.trim_left()
} else {
line
}
})
.fold((true, opener.to_owned()), |(first, mut acc), line| {
if !first {
acc.push('\n');
acc.push_str(&indent_str);
acc.push_str(line_start);
}
if line.len() > max_chars {
acc.push_str(&rewrite_string(line, &fmt));
} else {
if line.len() == 0 {
acc.pop(); // Remove space if this is an empty comment.
} else {
acc.push_str(line);
}
}
if line.len() > max_chars {
acc.push_str(&rewrite_string(line, &fmt));
} else {
if line.len() == 0 {
acc.pop(); // Remove space if this is an empty comment.
} else {
acc.push_str(line);
}
}
(false, acc)
});
(false, acc)
});
s.push_str(closer);
@ -146,24 +146,11 @@ pub fn find_comment_end(s: &str) -> Option<usize> {
}
}
#[test]
fn comment_end() {
assert_eq!(Some(6), find_comment_end("// hi\n"));
assert_eq!(Some(9), find_comment_end("/* sup */ "));
assert_eq!(Some(9), find_comment_end("/*/**/ */ "));
assert_eq!(Some(6), find_comment_end("/*/ */ weird!"));
assert_eq!(None, find_comment_end("/* hi /* test */"));
assert_eq!(None, find_comment_end("// hi /* test */"));
assert_eq!(Some(9), find_comment_end("// hi /*\n."));
}
/// Returns true if text contains any comment.
pub fn contains_comment(text: &str) -> bool {
CharClasses::new(text.chars()).any(|(kind, _)| kind == CodeCharKind::Comment )
CharClasses::new(text.chars()).any(|(kind, _)| kind == CodeCharKind::Comment)
}
struct CharClasses<T>
where T: Iterator,
T::Item: RichChar
@ -324,10 +311,14 @@ mod test {
// This is probably intended to be a non-test fn, but it is not used. I'm
// keeping it around unless it helps us test stuff.
fn uncommented(text: &str) -> String {
CharClasses::new(text.chars()).filter_map(|(s, c)| match s {
CodeCharKind::Normal => Some(c),
CodeCharKind::Comment => None
}).collect()
CharClasses::new(text.chars())
.filter_map(|(s, c)| {
match s {
CodeCharKind::Normal => Some(c),
CodeCharKind::Comment => None,
}
})
.collect()
}
#[test]

View File

@ -133,8 +133,8 @@ create_config! {
fn_args_density: Density,
fn_args_layout: StructLitStyle,
fn_arg_indent: BlockIndentStyle,
where_density: Density, // Should we at least try to put the where clause on the same line as
// the rest of the function decl?
where_density: Density, // Should we at least try to put the where clause on
// the same line as the rest of the function decl?
where_indent: BlockIndentStyle, // Visual will be treated like Tabbed
where_layout: ListTactic,
where_pred_indent: BlockIndentStyle,
@ -147,14 +147,14 @@ create_config! {
report_todo: ReportTactic,
report_fixme: ReportTactic,
reorder_imports: bool, // Alphabetically, case sensitive.
expr_indent_style: BlockIndentStyle,
closure_indent_style: BlockIndentStyle,
single_line_if_else: bool,
format_strings: bool,
chains_overflow_last: bool,
take_source_hints: bool, // Retain some formatting characteristics from
// the source code.
}
impl Default for Config {
fn default() -> Config {
Config {
max_width: 100,
@ -181,11 +181,10 @@ impl Default for Config {
report_todo: ReportTactic::Always,
report_fixme: ReportTactic::Never,
reorder_imports: false,
expr_indent_style: BlockIndentStyle::Tabbed,
closure_indent_style: BlockIndentStyle::Visual,
single_line_if_else: false,
format_strings: true,
chains_overflow_last: true,
take_source_hints: true,
}
}
}

View File

@ -9,6 +9,7 @@
// except according to those terms.
use std::cmp::Ordering;
use std::borrow::Borrow;
use rewrite::{Rewrite, RewriteContext};
use lists::{write_list, itemize_list, ListFormatting, SeparatorTactic, ListTactic};
@ -17,10 +18,11 @@ use StructLitStyle;
use utils::{span_after, make_indent, extra_offset, first_line_width, last_line_width, wrap_str,
binary_search};
use visitor::FmtVisitor;
use config::{BlockIndentStyle, MultilineStyle};
use config::MultilineStyle;
use comment::{FindUncommented, rewrite_comment, contains_comment};
use types::rewrite_path;
use items::{span_lo_for_arg, span_hi_for_arg, rewrite_fn_input};
use chains::rewrite_chain;
use syntax::{ast, ptr};
use syntax::codemap::{CodeMap, Span, BytePos, mk_sp};
@ -38,7 +40,7 @@ impl Rewrite for ast::Expr {
}
}
ast::Expr_::ExprCall(ref callee, ref args) => {
rewrite_call(context, callee, args, self.span, width, offset)
rewrite_call(context, &**callee, args, self.span, width, offset)
}
ast::Expr_::ExprParen(ref subexpr) => {
rewrite_paren(context, subexpr, width, offset)
@ -137,6 +139,11 @@ impl Rewrite for ast::Expr {
ast::Expr_::ExprClosure(capture, ref fn_decl, ref body) => {
rewrite_closure(capture, fn_decl, body, self.span, context, width, offset)
}
ast::Expr_::ExprField(..) |
ast::Expr_::ExprTupField(..) |
ast::Expr_::ExprMethodCall(..) => {
rewrite_chain(self, context, width, offset)
}
// We do not format these expressions yet, but they should still
// satisfy our width restrictions.
_ => wrap_str(context.snippet(self.span), context.config.max_width, width, offset),
@ -201,8 +208,6 @@ fn rewrite_closure(capture: ast::CaptureClause,
prefix.push_str(&ret_str);
}
let closure_indent = closure_indent(context, offset);
// Try to format closure body as a single line expression without braces.
if is_simple_block(body, context.codemap) && !prefix.contains('\n') {
let (spacer, closer) = if ret_str.is_empty() {
@ -233,17 +238,16 @@ fn rewrite_closure(capture: ast::CaptureClause,
// We couldn't format the closure body as a single line expression; fall
// back to block formatting.
let inner_context = context.overflow_context(closure_indent - context.block_indent);
let body_rewrite = body.expr
.as_ref()
.and_then(|body_expr| {
if let ast::Expr_::ExprBlock(ref inner) = body_expr.node {
Some(inner.rewrite(&inner_context, 2, 0))
Some(inner.rewrite(&context, 2, 0))
} else {
None
}
})
.unwrap_or_else(|| body.rewrite(&inner_context, 2, 0));
.unwrap_or_else(|| body.rewrite(&context, 2, 0));
Some(format!("{} {}", prefix, try_opt!(body_rewrite)))
}
@ -277,8 +281,10 @@ impl Rewrite for ast::Block {
};
if is_simple_block(self, context.codemap) && prefix.len() < width {
let body =
self.expr.as_ref().unwrap().rewrite(context, width - prefix.len(), offset);
let body = self.expr
.as_ref()
.unwrap()
.rewrite(context, width - prefix.len(), offset);
if let Some(ref expr_str) = body {
let result = format!("{}{{ {} }}", prefix, expr_str);
if result.len() <= width && !result.contains('\n') {
@ -393,9 +399,9 @@ impl<'a> Rewrite for Loop<'a> {
};
// FIXME: this drops any comment between "loop" and the block.
self.block.rewrite(context, width, offset).map(|result| {
format!("{}{}{} {}", label_string, self.keyword, pat_expr_string, result)
})
self.block
.rewrite(context, width, offset)
.map(|result| format!("{}{}{} {}", label_string, self.keyword, pat_expr_string, result))
}
}
@ -670,15 +676,13 @@ impl Rewrite for ast::Arm {
total_width += (pat_strs.len() - 1) * 3;
let mut vertical = total_width > pat_budget || pat_strs.iter().any(|p| p.contains('\n'));
if !vertical {
if !vertical && context.config.take_source_hints {
// If the patterns were previously stacked, keep them stacked.
// FIXME should be an option.
let pat_span = mk_sp(pats[0].span.lo, pats[pats.len() - 1].span.hi);
let pat_str = context.snippet(pat_span);
vertical = pat_str.find('\n').is_some();
}
let pats_width = if vertical {
pat_strs[pat_strs.len() - 1].len()
} else {
@ -717,9 +721,7 @@ impl Rewrite for ast::Arm {
// 4 = ` => `.len()
if context.config.max_width > line_start + comma.len() + 4 {
let budget = context.config.max_width - line_start - comma.len() - 4;
if let Some(ref body_str) = body.rewrite(context,
budget,
line_start + 4) {
if let Some(ref body_str) = body.rewrite(context, budget, line_start + 4) {
if first_line_width(body_str) <= budget {
return Some(format!("{}{} => {}{}",
attr_str.trim_left(),
@ -762,9 +764,7 @@ fn rewrite_guard(context: &RewriteContext,
// 4 = ` if `, 5 = ` => {`
let overhead = pattern_width + 4 + 5;
if overhead < width {
let cond_str = guard.rewrite(context,
width - overhead,
offset + pattern_width + 4);
let cond_str = guard.rewrite(context, width - overhead, offset + pattern_width + 4);
if let Some(cond_str) = cond_str {
return Some(format!(" if {}", cond_str));
}
@ -866,36 +866,35 @@ fn rewrite_string_lit(context: &RewriteContext,
Some(rewrite_string(str_lit, &fmt))
}
fn rewrite_call(context: &RewriteContext,
callee: &ast::Expr,
args: &[ptr::P<ast::Expr>],
span: Span,
width: usize,
offset: usize)
-> Option<String> {
let callback = |callee_max_width| {
rewrite_call_inner(context,
callee,
callee_max_width,
args,
span,
width,
offset)
};
pub fn rewrite_call<R>(context: &RewriteContext,
callee: &R,
args: &[ptr::P<ast::Expr>],
span: Span,
width: usize,
offset: usize)
-> Option<String>
where R: Rewrite
{
let closure = |callee_max_width| {
rewrite_call_inner(context, callee, callee_max_width, args, span, width, offset)
};
// 2 is for parens
let max_width = try_opt!(width.checked_sub(2));
binary_search(1, max_width, callback)
binary_search(1, max_width, closure)
}
fn rewrite_call_inner(context: &RewriteContext,
callee: &ast::Expr,
max_callee_width: usize,
args: &[ptr::P<ast::Expr>],
span: Span,
width: usize,
offset: usize)
-> Result<String, Ordering> {
fn rewrite_call_inner<R>(context: &RewriteContext,
callee: &R,
max_callee_width: usize,
args: &[ptr::P<ast::Expr>],
span: Span,
width: usize,
offset: usize)
-> Result<String, Ordering>
where R: Rewrite
{
let callee = callee.borrow();
// FIXME using byte lens instead of char lens (and probably all over the
// place too)
let callee_str = match callee.rewrite(context, max_callee_width, offset) {
@ -909,6 +908,9 @@ fn rewrite_call_inner(context: &RewriteContext,
None => return Err(Ordering::Greater),
};
let span_lo = span_after(span, "(", context.codemap);
let span = mk_sp(span_lo, span.hi);
let extra_offset = extra_offset(&callee_str, offset);
// 2 is for parens.
let remaining_width = match width.checked_sub(extra_offset + 2) {
@ -916,8 +918,12 @@ fn rewrite_call_inner(context: &RewriteContext,
None => return Err(Ordering::Greater),
};
let offset = offset + extra_offset + 1;
let inner_indent = expr_indent(context, offset);
let inner_context = context.overflow_context(inner_indent - context.block_indent);
let block_indent = if args.len() == 1 {
context.block_indent
} else {
offset
};
let inner_context = &RewriteContext { block_indent: block_indent, ..*context };
let items = itemize_list(context.codemap,
args.iter(),
@ -929,7 +935,7 @@ fn rewrite_call_inner(context: &RewriteContext,
item.rewrite(&inner_context, remaining_width, offset)
.unwrap_or(context.snippet(item.span))
},
callee.span.hi + BytePos(1),
span.lo,
span.hi);
let fmt = ListFormatting::for_fn(remaining_width, offset);
@ -941,21 +947,6 @@ fn rewrite_call_inner(context: &RewriteContext,
Ok(format!("{}({})", callee_str, list_str))
}
macro_rules! block_indent_helper {
($name:ident, $option:ident) => (
fn $name(context: &RewriteContext, offset: usize) -> usize {
match context.config.$option {
BlockIndentStyle::Inherit => context.block_indent,
BlockIndentStyle::Tabbed => context.block_indent + context.config.tab_spaces,
BlockIndentStyle::Visual => offset,
}
}
);
}
block_indent_helper!(expr_indent, expr_indent_style);
block_indent_helper!(closure_indent, closure_indent_style);
fn rewrite_paren(context: &RewriteContext,
subexpr: &ast::Expr,
width: usize,
@ -1004,7 +995,8 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
}
};
let field_iter = fields.into_iter().map(StructLitField::Regular)
let field_iter = fields.into_iter()
.map(StructLitField::Regular)
.chain(base.into_iter().map(StructLitField::Base));
let inner_context = &RewriteContext { block_indent: indent, ..*context };
@ -1016,10 +1008,11 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
match *item {
StructLitField::Regular(ref field) => field.span.lo,
StructLitField::Base(ref expr) => {
let last_field_hi =
fields.last().map_or(span.lo, |field| field.span.hi);
let snippet =
context.snippet(mk_sp(last_field_hi, expr.span.lo));
let last_field_hi = fields.last()
.map_or(span.lo,
|field| field.span.hi);
let snippet = context.snippet(mk_sp(last_field_hi,
expr.span.lo));
let pos = snippet.find_uncommented("..").unwrap();
last_field_hi + BytePos(pos as u32)
}
@ -1035,7 +1028,7 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
match *item {
StructLitField::Regular(ref field) => {
rewrite_field(inner_context, &field, h_budget, indent)
.unwrap_or(context.snippet(field.span))
.unwrap_or(context.snippet(field.span))
}
StructLitField::Base(ref expr) => {
// 2 = ..
@ -1067,11 +1060,10 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
let fields_str = try_opt!(write_list(&items.collect::<Vec<_>>(), &fmt));
let format_on_newline = || {
let inner_indent = make_indent(context.block_indent +
context.config.tab_spaces);
let outer_indent = make_indent(context.block_indent);
Some(format!("{} {{\n{}{}\n{}}}", path_str, inner_indent, fields_str, outer_indent))
};
let inner_indent = make_indent(context.block_indent + context.config.tab_spaces);
let outer_indent = make_indent(context.block_indent);
Some(format!("{} {{\n{}{}\n{}}}", path_str, inner_indent, fields_str, outer_indent))
};
match (context.config.struct_lit_style, context.config.struct_lit_multiline_style) {
(StructLitStyle::Block, _) if fields_str.contains('\n') => format_on_newline(),
@ -1090,8 +1082,8 @@ fn rewrite_field(context: &RewriteContext,
-> Option<String> {
let name = &field.ident.node.to_string();
let overhead = name.len() + 2;
let expr =
field.expr.rewrite(context, try_opt!(width.checked_sub(overhead)), offset + overhead);
let expr = field.expr
.rewrite(context, try_opt!(width.checked_sub(overhead)), offset + overhead);
expr.map(|s| format!("{}: {}", name, s))
}
@ -1152,10 +1144,7 @@ fn rewrite_binary_op(context: &RewriteContext,
// worth trying to put everything on one line.
if rhs_result.len() + 2 + operator_str.len() < width && !rhs_result.contains('\n') {
// 1 = space between lhs expr and operator
if let Some(mut result) = lhs.rewrite(context,
width - 1 - operator_str.len(),
offset) {
if let Some(mut result) = lhs.rewrite(context, width - 1 - operator_str.len(), offset) {
result.push(' ');
result.push_str(&operator_str);
result.push(' ');
@ -1167,9 +1156,7 @@ fn rewrite_binary_op(context: &RewriteContext,
return Some(result);
}
if let Some(rhs_result) = rhs.rewrite(context,
remaining_width,
offset + result.len()) {
if let Some(rhs_result) = rhs.rewrite(context, remaining_width, offset + result.len()) {
if rhs_result.len() <= remaining_width {
result.push_str(&rhs_result);
return Some(result);

View File

@ -38,10 +38,10 @@ impl Rewrite for ast::ViewPath {
let path_str = try_opt!(path.rewrite(context, width - ident_str.len() - 4, offset));
Some(if path.segments.last().unwrap().identifier == ident {
path_str
} else {
format!("{} as {}", path_str, ident_str)
})
path_str
} else {
format!("{} as {}", path_str, ident_str)
})
}
}
}
@ -150,10 +150,10 @@ pub fn rewrite_use_list(width: usize,
let list_str = try_opt!(write_list(&items[first_index..], &fmt));
Some(if path_str.is_empty() {
format!("{{{}}}", list_str)
} else {
format!("{}::{{{}}}", path_str, list_str)
})
format!("{{{}}}", list_str)
} else {
format!("{}::{{{}}}", path_str, list_str)
})
}
// Returns true when self item was found.

View File

@ -334,11 +334,13 @@ impl<'a> FmtVisitor<'a> {
// FIXME: the comment for the self argument is dropped. This is blocked
// on rust issue #27522.
let min_args = explicit_self.and_then(|explicit_self| {
rewrite_explicit_self(explicit_self, args)
}).map(|self_str| {
arg_item_strs[0] = self_str;
2
}).unwrap_or(1);
rewrite_explicit_self(explicit_self, args)
})
.map(|self_str| {
arg_item_strs[0] = self_str;
2
})
.unwrap_or(1);
// Comments between args
let mut arg_items = Vec::new();
@ -511,8 +513,9 @@ impl<'a> FmtVisitor<'a> {
|arg| arg.ty.span.hi,
|arg| {
// FIXME silly width, indent
arg.ty.rewrite(&self.get_context(), 1000, 0)
.unwrap()
arg.ty
.rewrite(&self.get_context(), 1000, 0)
.unwrap()
},
span_after(field.span, "(", self.codemap),
next_span_start);
@ -760,11 +763,12 @@ impl<'a> FmtVisitor<'a> {
let typ = field.node.ty.rewrite(&self.get_context(), 1000, 0).unwrap();
let indent = self.block_indent + self.config.tab_spaces;
let mut attr_str = field.node.attrs
.rewrite(&self.get_context(),
self.config.max_width - indent,
indent)
.unwrap();
let mut attr_str = field.node
.attrs
.rewrite(&self.get_context(),
self.config.max_width - indent,
indent)
.unwrap();
if !attr_str.is_empty() {
attr_str.push('\n');
attr_str.push_str(&make_indent(indent));
@ -803,12 +807,9 @@ impl<'a> FmtVisitor<'a> {
// Strings for the generics.
let context = self.get_context();
// FIXME: don't unwrap
let lt_strs = lifetimes.iter().map(|lt| {
lt.rewrite(&context, h_budget, offset).unwrap()
});
let ty_strs = tys.iter().map(|ty_param| {
ty_param.rewrite(&context, h_budget, offset).unwrap()
});
let lt_strs = lifetimes.iter().map(|lt| lt.rewrite(&context, h_budget, offset).unwrap());
let ty_strs = tys.iter()
.map(|ty_param| ty_param.rewrite(&context, h_budget, offset).unwrap());
// Extract comments between generics.
let lt_spans = lifetimes.iter().map(|l| {

View File

@ -10,6 +10,7 @@
#![feature(rustc_private)]
#![feature(custom_attribute)]
#![feature(slice_splits)]
#![allow(unused_attributes)]
// TODO we're going to allocate a whole bunch of temp Strings, is it worth
@ -70,6 +71,7 @@ mod string;
mod comment;
mod modules;
pub mod rustfmt_diff;
mod chains;
const MIN_STRING: usize = 10;
// When we get scoped annotations, we should have rustfmt::skip.

View File

@ -272,9 +272,10 @@ impl<'a, T, I, F1, F2, F3> Iterator for ListItems<'a, I, F1, F2, F3>
self.inner.next().map(|item| {
let mut new_lines = false;
// Pre-comment
let pre_snippet = self.codemap.span_to_snippet(codemap::mk_sp(self.prev_span_end,
(self.get_lo)(&item)))
.unwrap();
let pre_snippet = self.codemap
.span_to_snippet(codemap::mk_sp(self.prev_span_end,
(self.get_lo)(&item)))
.unwrap();
let trimmed_pre_snippet = pre_snippet.trim();
let pre_comment = if !trimmed_pre_snippet.is_empty() {
Some(trimmed_pre_snippet.to_owned())
@ -285,11 +286,12 @@ impl<'a, T, I, F1, F2, F3> Iterator for ListItems<'a, I, F1, F2, F3>
// Post-comment
let next_start = match self.inner.peek() {
Some(ref next_item) => (self.get_lo)(next_item),
None => self.next_span_start
None => self.next_span_start,
};
let post_snippet = self.codemap.span_to_snippet(codemap::mk_sp((self.get_hi)(&item),
next_start))
.unwrap();
let post_snippet = self.codemap
.span_to_snippet(codemap::mk_sp((self.get_hi)(&item),
next_start))
.unwrap();
let comment_end = match self.inner.peek() {
Some(..) => {
@ -315,12 +317,11 @@ impl<'a, T, I, F1, F2, F3> Iterator for ListItems<'a, I, F1, F2, F3>
}
// Potential *single* line comment.
(_, Some(j)) => j + 1,
_ => post_snippet.len()
_ => post_snippet.len(),
}
},
}
None => {
post_snippet.find_uncommented(self.terminator)
.unwrap_or(post_snippet.len())
post_snippet.find_uncommented(self.terminator).unwrap_or(post_snippet.len())
}
};

View File

@ -17,21 +17,20 @@ impl<'a> FmtVisitor<'a> {
// TODO these format_missing methods are ugly. Refactor and add unit tests
// for the central whitespace stripping loop.
pub fn format_missing(&mut self, end: BytePos) {
self.format_missing_inner(end, |this, last_snippet, _| {
this.buffer.push_str(last_snippet)
})
self.format_missing_inner(end, |this, last_snippet, _| this.buffer.push_str(last_snippet))
}
pub fn format_missing_with_indent(&mut self, end: BytePos) {
self.format_missing_inner(end, |this, last_snippet, snippet| {
this.buffer.push_str(last_snippet.trim_right());
if last_snippet == snippet {
self.format_missing_inner(end,
|this, last_snippet, snippet| {
this.buffer.push_str(last_snippet.trim_right());
if last_snippet == snippet {
// No new lines in the snippet.
this.buffer.push_str("\n");
}
let indent = make_indent(this.block_indent);
this.buffer.push_str(&indent);
})
this.buffer.push_str("\n");
}
let indent = make_indent(this.block_indent);
this.buffer.push_str(&indent);
})
}
fn format_missing_inner<F: Fn(&mut FmtVisitor, &str, &str)>(&mut self,
@ -60,9 +59,7 @@ impl<'a> FmtVisitor<'a> {
let span = codemap::mk_sp(start, end);
let snippet = self.snippet(span);
self.write_snippet(&snippet,
true,
&process_last_snippet);
self.write_snippet(&snippet, true, &process_last_snippet);
}
fn write_snippet<F: Fn(&mut FmtVisitor, &str, &str)>(&mut self,

View File

@ -44,8 +44,8 @@ pub fn rewrite_string<'a>(s: &str, fmt: &StringFormat<'a>) -> String {
result.push_str(fmt.opener);
let ender_length = fmt.line_end.len();
let max_chars = fmt.width.checked_sub(fmt.opener.len()).unwrap_or(0)
.checked_sub(ender_length).unwrap_or(1);
let max_chars = fmt.width.checked_sub(fmt.opener.len() + ender_length).unwrap_or(1);
loop {
let mut cur_end = cur_start + max_chars;

View File

@ -130,16 +130,16 @@ impl<'a> Rewrite for SegmentParam<'a> {
// FIXME doesn't always use width, offset
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
Some(match *self {
SegmentParam::LifeTime(ref lt) => {
pprust::lifetime_to_string(lt)
}
SegmentParam::Type(ref ty) => {
try_opt!(ty.rewrite(context, width, offset))
}
SegmentParam::Binding(ref binding) => {
format!("{} = {}", binding.ident, try_opt!(binding.ty.rewrite(context, width, offset)))
}
})
SegmentParam::LifeTime(ref lt) => {
pprust::lifetime_to_string(lt)
}
SegmentParam::Type(ref ty) => {
try_opt!(ty.rewrite(context, width, offset))
}
SegmentParam::Binding(ref binding) => {
format!("{} = {}", binding.ident, try_opt!(binding.ty.rewrite(context, width, offset)))
}
})
}
}
@ -196,13 +196,12 @@ fn rewrite_segment(segment: &ast::PathSegment,
ast::PathParameters::AngleBracketedParameters(ref data) if !data.lifetimes.is_empty() ||
!data.types.is_empty() ||
!data.bindings.is_empty() => {
let param_list = data.lifetimes.iter()
.map(SegmentParam::LifeTime)
.chain(data.types.iter()
.map(|x| SegmentParam::Type(&*x)))
.chain(data.bindings.iter()
.map(|x| SegmentParam::Binding(&*x)))
.collect::<Vec<_>>();
let param_list = data.lifetimes
.iter()
.map(SegmentParam::LifeTime)
.chain(data.types.iter().map(|x| SegmentParam::Type(&*x)))
.chain(data.bindings.iter().map(|x| SegmentParam::Binding(&*x)))
.collect::<Vec<_>>();
let next_span_lo = param_list.last().unwrap().get_span().hi + BytePos(1);
let list_lo = span_after(codemap::mk_sp(*span_lo, span_hi), "<", context.codemap);
@ -274,57 +273,67 @@ impl Rewrite for ast::WherePredicate {
// TODO dead spans?
// TODO assumes we'll always fit on one line...
Some(match *self {
ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate{ref bound_lifetimes,
ref bounded_ty,
ref bounds,
..}) => {
if !bound_lifetimes.is_empty() {
let lifetime_str = bound_lifetimes.iter().map(|lt| {
lt.rewrite(context, width, offset).unwrap()
}).collect::<Vec<_>>().join(", ");
let type_str = pprust::ty_to_string(bounded_ty);
ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { ref bound_lifetimes,
ref bounded_ty,
ref bounds,
.. }) => {
if !bound_lifetimes.is_empty() {
let lifetime_str = bound_lifetimes.iter()
.map(|lt| {
lt.rewrite(context, width, offset)
.unwrap()
})
.collect::<Vec<_>>()
.join(", ");
let type_str = pprust::ty_to_string(bounded_ty);
// 8 = "for<> : ".len()
let used_width = lifetime_str.len() + type_str.len() + 8;
let bounds_str = bounds.iter().map(|ty_bound| {
ty_bound.rewrite(context,
width - used_width,
offset + used_width)
.unwrap()
}).collect::<Vec<_>>().join(" + ");
let used_width = lifetime_str.len() + type_str.len() + 8;
let bounds_str = bounds.iter()
.map(|ty_bound| {
ty_bound.rewrite(context,
width - used_width,
offset + used_width)
.unwrap()
})
.collect::<Vec<_>>()
.join(" + ");
format!("for<{}> {}: {}", lifetime_str, type_str, bounds_str)
} else {
let type_str = pprust::ty_to_string(bounded_ty);
format!("for<{}> {}: {}", lifetime_str, type_str, bounds_str)
} else {
let type_str = pprust::ty_to_string(bounded_ty);
// 2 = ": ".len()
let used_width = type_str.len() + 2;
let bounds_str = bounds.iter().map(|ty_bound| {
ty_bound.rewrite(context,
width - used_width,
offset + used_width)
.unwrap()
}).collect::<Vec<_>>().join(" + ");
let used_width = type_str.len() + 2;
let bounds_str = bounds.iter()
.map(|ty_bound| {
ty_bound.rewrite(context,
width - used_width,
offset + used_width)
.unwrap()
})
.collect::<Vec<_>>()
.join(" + ");
format!("{}: {}", type_str, bounds_str)
}
format!("{}: {}", type_str, bounds_str)
}
ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate{ref lifetime,
ref bounds,
..}) => {
format!("{}: {}",
}
ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { ref lifetime,
ref bounds,
.. }) => {
format!("{}: {}",
pprust::lifetime_to_string(lifetime),
bounds.iter().map(pprust::lifetime_to_string)
.collect::<Vec<_>>().join(" + "))
}
ast::WherePredicate::EqPredicate(ast::WhereEqPredicate{ref path, ref ty, ..}) => {
let ty_str = pprust::ty_to_string(ty);
}
ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { ref path, ref ty, .. }) => {
let ty_str = pprust::ty_to_string(ty);
// 3 = " = ".len()
let used_width = 3 + ty_str.len();
let path_str = try_opt!(path.rewrite(context,
let used_width = 3 + ty_str.len();
let path_str = try_opt!(path.rewrite(context,
width - used_width,
offset + used_width));
format!("{} = {}", path_str, ty_str)
}
})
format!("{} = {}", path_str, ty_str)
}
})
}
}
@ -359,8 +368,9 @@ impl Rewrite for ast::TyParamBound {
impl Rewrite for ast::TyParamBounds {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
let strs: Vec<_> =
self.iter().map(|b| b.rewrite(context, width, offset).unwrap()).collect();
let strs: Vec<_> = self.iter()
.map(|b| b.rewrite(context, width, offset).unwrap())
.collect();
Some(strs.join(" + "))
}
}
@ -373,9 +383,11 @@ impl Rewrite for ast::TyParam {
if !self.bounds.is_empty() {
result.push_str(": ");
let bounds = self.bounds.iter().map(|ty_bound| {
ty_bound.rewrite(context, width, offset).unwrap()
}).collect::<Vec<_>>().join(" + ");
let bounds = self.bounds
.iter()
.map(|ty_bound| ty_bound.rewrite(context, width, offset).unwrap())
.collect::<Vec<_>>()
.join(" + ");
result.push_str(&bounds);
}
@ -392,9 +404,11 @@ impl Rewrite for ast::TyParam {
impl Rewrite for ast::PolyTraitRef {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
if !self.bound_lifetimes.is_empty() {
let lifetime_str = self.bound_lifetimes.iter().map(|lt| {
lt.rewrite(context, width, offset).unwrap()
}).collect::<Vec<_>>().join(", ");
let lifetime_str = self.bound_lifetimes
.iter()
.map(|lt| lt.rewrite(context, width, offset).unwrap())
.collect::<Vec<_>>()
.join(", ");
// 6 is "for<> ".len()
let extra_offset = lifetime_str.len() + 6;
let max_path_width = try_opt!(width.checked_sub(extra_offset));

View File

@ -14,6 +14,7 @@ use syntax::ast::{self, Visibility, Attribute, MetaItem, MetaItem_};
use syntax::codemap::{CodeMap, Span, BytePos};
use comment::FindUncommented;
use rewrite::{Rewrite, RewriteContext};
use SKIP_ANNOTATION;
@ -93,10 +94,16 @@ pub fn contains_skip(attrs: &[Attribute]) -> bool {
// Find the end of a TyParam
#[inline]
pub fn end_typaram(typaram: &ast::TyParam) -> BytePos {
typaram.bounds.last().map(|bound| match *bound {
ast::RegionTyParamBound(ref lt) => lt.span,
ast::TraitTyParamBound(ref prt, _) => prt.span,
}).unwrap_or(typaram.span).hi
typaram.bounds
.last()
.map(|bound| {
match *bound {
ast::RegionTyParamBound(ref lt) => lt.span,
ast::TraitTyParamBound(ref prt, _) => prt.span,
}
})
.unwrap_or(typaram.span)
.hi
}
#[inline]
@ -202,6 +209,12 @@ pub fn wrap_str<S: AsRef<str>>(s: S, max_width: usize, width: usize, offset: usi
Some(s)
}
impl Rewrite for String {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
wrap_str(self, context.config.max_width, width, offset).map(ToOwned::to_owned)
}
}
// Binary search in integer range. Returns the first Ok value returned by the
// callback.
// The callback takes an integer and returns either an Ok, or an Err indicating
@ -231,13 +244,13 @@ pub fn binary_search<C, T>(mut lo: usize, mut hi: usize, callback: C) -> Option<
#[test]
fn bin_search_test() {
let closure = |i| {
match i {
4 => Ok(()),
j if j > 4 => Err(Ordering::Less),
j if j < 4 => Err(Ordering::Greater),
_ => unreachable!(),
}
};
match i {
4 => Ok(()),
j if j > 4 => Err(Ordering::Less),
j if j < 4 => Err(Ordering::Greater),
_ => unreachable!(),
}
};
assert_eq!(Some(()), binary_search(1, 10, &closure));
assert_eq!(None, binary_search(1, 3, &closure));

View File

@ -202,19 +202,11 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> {
}
ast::Item_::ItemStruct(ref def, ref generics) => {
self.format_missing_with_indent(item.span.lo);
self.visit_struct(item.ident,
item.vis,
def,
generics,
item.span);
self.visit_struct(item.ident, item.vis, def, generics, item.span);
}
ast::Item_::ItemEnum(ref def, ref generics) => {
self.format_missing_with_indent(item.span.lo);
self.visit_enum(item.ident,
item.vis,
def,
generics,
item.span);
self.visit_enum(item.ident, item.vis, def, generics, item.span);
self.last_pos = item.span.hi;
}
ast::Item_::ItemMod(ref module) => {
@ -236,10 +228,7 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> {
self.format_missing_with_indent(ti.span.lo);
let indent = self.block_indent;
let new_fn = self.rewrite_required_fn(indent,
ti.ident,
sig,
ti.span);
let new_fn = self.rewrite_required_fn(indent, ti.ident, sig, ti.span);
if let Some(fn_str) = new_fn {

View File

@ -21,7 +21,7 @@ enum_trailing_comma = true
report_todo = "Always"
report_fixme = "Never"
reorder_imports = false
expr_indent_style = "Tabbed"
closure_indent_style = "Visual"
single_line_if_else = false
format_strings = true
chains_overflow_last = true
take_source_hints = true

View File

@ -0,0 +1,37 @@
// rustfmt-chains_overflow_last: false
// Test chain formatting without overflowing the last item.
fn main() {
bbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccccccccccccc
.ddddddddddddddddddddddddddd();
bbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccccccccccccc.ddddddddddddddddddddddddddd.eeeeeeee();
x()
.y(|| match cond() { true => (), false => () });
loong_func()
.quux(move || if true {
1
} else {
2
});
fffffffffffffffffffffffffffffffffff(a,
{
SCRIPT_TASK_ROOT
.with(|root| {
*root.borrow_mut() = Some(&script_task);
});
});
let suuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuum = xxxxxxx
.map(|x| x + 5)
.map(|x| x / 2)
.fold(0, |acc, x| acc + x);
aaaaaaaaaaaaaaaa.map(|x| {
x += 1;
x
}).filter(some_mod::some_filter)
}

43
tests/source/chains.rs Normal file
View File

@ -0,0 +1,43 @@
// Test chain formatting.
fn main() {
// Don't put chains on a single line if it wasn't so in source.
let a = b .c
.d.1
.foo(|x| x + 1);
bbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccccccccccccc
.ddddddddddddddddddddddddddd();
bbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccccccccccccc.ddddddddddddddddddddddddddd.eeeeeeee();
// Test case where first chain element isn't a path, but is shorter than
// the size of a tab.
x()
.y(|| match cond() { true => (), false => () });
loong_func()
.quux(move || if true {
1
} else {
2
});
fffffffffffffffffffffffffffffffffff(a,
{
SCRIPT_TASK_ROOT
.with(|root| {
*root.borrow_mut() = Some(&script_task);
});
});
let suuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuum = xxxxxxx
.map(|x| x + 5)
.map(|x| x / 2)
.fold(0, |acc, x| acc + x);
aaaaaaaaaaaaaaaa.map(|x| {
x += 1;
x
}).filter(some_mod::some_filter)
}

View File

@ -0,0 +1,8 @@
// rustfmt-take_source_hints: false
// We know best!
fn main() {
a.b
.c
.d();
}

View File

@ -1,9 +0,0 @@
// rustfmt-expr_indent_style: Visual
// Visual level block indentation.
fn matcher() {
Some(while true {
test();
})
}

View File

@ -30,5 +30,5 @@ formatting"#;
let unicode3 = "中华Việt Nam";
let unicode4 = "☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃";
"stuff"
"stuffin'"
}

View File

@ -121,9 +121,8 @@ pub fn idempotent_check(filename: String) -> Result<(), HashMap<String, Vec<Mism
// multithreaded rustfmt
thread::catch_panic(move || {
run(args, WriteMode::Return(HANDLE_RESULT), config);
}).map_err(|any|
*any.downcast().ok().expect("Downcast failed.")
)
})
.map_err(|any| *any.downcast().ok().expect("Downcast failed."))
}
@ -138,7 +137,9 @@ fn get_config(config_file: Option<&str>) -> Box<Config> {
}
};
let mut def_config_file = fs::File::open(config_file_name).ok().expect("Couldn't open config.");
let mut def_config_file = fs::File::open(config_file_name)
.ok()
.expect("Couldn't open config.");
let mut def_config = String::new();
def_config_file.read_to_string(&mut def_config).ok().expect("Couldn't read config.");
@ -148,14 +149,17 @@ fn get_config(config_file: Option<&str>) -> Box<Config> {
// Reads significant comments of the form: // rustfmt-key: value
// into a hash map.
fn read_significant_comments(file_name: &str) -> HashMap<String, String> {
let file = fs::File::open(file_name).ok().expect(&format!("Couldn't read file {}.", file_name));
let file = fs::File::open(file_name)
.ok()
.expect(&format!("Couldn't read file {}.", file_name));
let reader = BufReader::new(file);
let pattern = r"^\s*//\s*rustfmt-([^:]+):\s*(\S+)";
let regex = regex::Regex::new(&pattern).ok().expect("Failed creating pattern 1.");
// Matches lines containing significant comments or whitespace.
let line_regex = regex::Regex::new(r"(^\s*$)|(^\s*//\s*rustfmt-[^:]+:\s*\S+)")
.ok().expect("Failed creating pattern 2.");
.ok()
.expect("Failed creating pattern 2.");
reader.lines()
.map(|line| line.ok().expect("Failed getting line."))

View File

@ -0,0 +1,45 @@
// rustfmt-chains_overflow_last: false
// Test chain formatting without overflowing the last item.
fn main() {
bbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccccccccccccc.ddddddddddddddddddddddddddd();
bbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccccccccccccc
.ddddddddddddddddddddddddddd
.eeeeeeee();
x().y(|| {
match cond() {
true => (),
false => (),
}
});
loong_func()
.quux(move || {
if true {
1
} else {
2
}
});
fffffffffffffffffffffffffffffffffff(a,
{
SCRIPT_TASK_ROOT.with(|root| {
*root.borrow_mut() =
Some(&script_task);
});
});
let suuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuum = xxxxxxx.map(|x| x + 5)
.map(|x| x / 2)
.fold(0,
|acc, x| acc + x);
aaaaaaaaaaaaaaaa.map(|x| {
x += 1;
x
})
.filter(some_mod::some_filter)
}

View File

@ -0,0 +1,16 @@
// rustfmt-chains_overflow_last: false
fn main() {
reader.lines()
.map(|line| line.ok().expect("Failed getting line."))
.take_while(|line| line_regex.is_match(&line))
.filter_map(|line| {
regex.captures_iter(&line)
.next()
.map(|capture| {
(capture.at(1).expect("Couldn\'t unwrap capture.").to_owned(),
capture.at(2).expect("Couldn\'t unwrap capture.").to_owned())
})
})
.collect();
}

50
tests/target/chains.rs Normal file
View File

@ -0,0 +1,50 @@
// Test chain formatting.
fn main() {
// Don't put chains on a single line if it wasn't so in source.
let a = b.c
.d
.1
.foo(|x| x + 1);
bbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccccccccccccc.ddddddddddddddddddddddddddd();
bbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccccccccccccc
.ddddddddddddddddddddddddddd
.eeeeeeee();
// Test case where first chain element isn't a path, but is shorter than
// the size of a tab.
x().y(|| {
match cond() {
true => (),
false => (),
}
});
loong_func().quux(move || {
if true {
1
} else {
2
}
});
fffffffffffffffffffffffffffffffffff(a,
{
SCRIPT_TASK_ROOT.with(|root| {
*root.borrow_mut() = Some(&script_task);
});
});
let suuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuum = xxxxxxx.map(|x| x + 5)
.map(|x| x / 2)
.fold(0,
|acc, x| acc + x);
aaaaaaaaaaaaaaaa.map(|x| {
x += 1;
x
})
.filter(some_mod::some_filter)
}

View File

@ -9,52 +9,51 @@ fn main() {
b: WithType, // argument
// ignored
_| {
(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb)
};
(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb)
};
let block_body = move |xxxxxxxxxxxxxxxxxxxxxxxxxxxxx,
ref yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy| {
xxxxxxxxxxxxxxxxxxxxxxxxxxxxx + yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
};
xxxxxxxxxxxxxxxxxxxxxxxxxxxxx + yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
};
let loooooooooooooong_name = |field| {
// TODO(#27): format comments.
if field.node.attrs.len() > 0 {
field.node.attrs[0].span.lo
} else {
field.span.lo
}
};
if field.node.attrs.len() > 0 {
field.node.attrs[0].span.lo
} else {
field.span.lo
}
};
let block_me = |field| {
if true_story() {
1
} else {
2
}
};
if true_story() {
1
} else {
2
}
};
let unblock_me = |trivial| closure();
let empty = |arg| {};
let simple = |arg| { /* TODO(#27): comment formatting */
foo(arg)
};
foo(arg)
};
let test = || {
do_something();
do_something_else();
};
do_something();
do_something_else();
};
let arg_test = |big_argument_name, test123| {
looooooooooooooooooong_function_naaaaaaaaaaaaaaaaame()
};
looooooooooooooooooong_function_naaaaaaaaaaaaaaaaame()
};
let arg_test = |big_argument_name, test123| {
looooooooooooooooooong_function_naaaaaaaaaaaaaaaaame()
};
looooooooooooooooooong_function_naaaaaaaaaaaaaaaaame()
};
let simple_closure = move || -> () {};
@ -63,8 +62,8 @@ fn main() {
let closure_with_return_type = |aaaaaaaaaaaaaaaaaaaaaaarg1,
aaaaaaaaaaaaaaaaaaaaaaarg2|
-> Strong {
"sup".to_owned()
};
"sup".to_owned()
};
|arg1, arg2, _, _, arg3, arg4| {
let temp = arg4 + arg3;

View File

@ -0,0 +1,6 @@
// rustfmt-take_source_hints: false
// We know best!
fn main() {
a.b.c.d();
}

View File

@ -1,9 +0,0 @@
// rustfmt-expr_indent_style: Visual
// Visual level block indentation.
fn matcher() {
Some(while true {
test();
})
}

View File

@ -89,13 +89,13 @@ fn bar() {
}
syntactically_correct(loop {
sup('?');
},
sup('?');
},
if cond {
0
} else {
1
});
0
} else {
1
});
let third = ..10;
let infi_range = ..;

View File

@ -57,7 +57,7 @@ fn foo() {
// Test that a match on an overflow line is laid out properly.
fn main() {
let sub_span =
match self.span.sub_span_after_keywooooooooooooooooooooord(use_item.span, keywords::As) {
match xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx {
Some(sub_span) => Some(sub_span),
None => sub_span,
};

View File

@ -36,5 +36,5 @@ formatting"#;
let unicode4 = "☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃\
";
"stuff"
"stuffin'"
}