Merge pull request #290 from SiegeLord/tabs

Initial implementation of hard tab indentation.
This commit is contained in:
Marcus Klaas de Vries 2015-09-20 12:27:16 +02:00
commit ce2c4f6be6
17 changed files with 514 additions and 210 deletions

View File

@ -19,8 +19,9 @@
// we put each subexpression on a separate, much like the (default) function
// argument function argument strategy.
use Indent;
use rewrite::{Rewrite, RewriteContext};
use utils::{first_line_width, make_indent};
use utils::first_line_width;
use expr::rewrite_call;
use syntax::{ast, ptr};
@ -30,7 +31,7 @@
pub fn rewrite_chain(mut expr: &ast::Expr,
context: &RewriteContext,
width: usize,
offset: usize)
offset: Indent)
-> Option<String> {
let total_span = expr.span;
let mut subexpr_list = vec![expr];
@ -116,7 +117,7 @@ pub fn rewrite_chain(mut expr: &ast::Expr,
let connector = if fits_single_line {
String::new()
} else {
format!("\n{}", make_indent(indent))
format!("\n{}", indent.to_string(context.config))
};
let first_connector = if extend {
@ -145,7 +146,7 @@ fn rewrite_chain_expr(expr: &ast::Expr,
span: Span,
context: &RewriteContext,
width: usize,
offset: usize)
offset: Indent)
-> Option<String> {
match expr.node {
ast::Expr_::ExprMethodCall(ref method_name, ref types, ref expressions) => {
@ -179,7 +180,7 @@ fn rewrite_method_call(method_name: ast::Ident,
span: Span,
context: &RewriteContext,
width: usize,
offset: usize)
offset: Indent)
-> Option<String> {
let type_str = if types.is_empty() {
String::new()

View File

@ -12,10 +12,16 @@
use std::iter;
use Indent;
use config::Config;
use string::{StringFormat, rewrite_string};
use utils::make_indent;
pub fn rewrite_comment(orig: &str, block_style: bool, width: usize, offset: usize) -> String {
pub fn rewrite_comment(orig: &str,
block_style: bool,
width: usize,
offset: Indent,
config: &Config)
-> String {
let s = orig.trim();
// Edge case: block comments. Let's not trim their lines (for now).
@ -33,11 +39,12 @@ pub fn rewrite_comment(orig: &str, block_style: bool, width: usize, offset: usiz
line_start: line_start,
line_end: "",
width: max_chars,
offset: offset + opener.len() - line_start.len(),
offset: offset + (opener.len() - line_start.len()),
trim_end: true,
config: config,
};
let indent_str = make_indent(offset);
let indent_str = offset.to_string(config);
let line_breaks = s.chars().filter(|&c| c == '\n').count();
let (_, mut s) = s.lines()
@ -288,27 +295,32 @@ fn next(&mut self) -> Option<(CodeCharKind, T::Item)> {
mod test {
use super::{CharClasses, CodeCharKind, contains_comment, rewrite_comment, FindUncommented};
// FIXME(#217): prevent string literal from going over the limit.
use Indent;
#[test]
#[rustfmt_skip]
fn format_comments() {
assert_eq!("/* test */", rewrite_comment(" //test", true, 100, 100));
assert_eq!("// comment\n// on a", rewrite_comment("// comment on a", false, 10, 0));
let config = Default::default();
assert_eq!("/* test */", rewrite_comment(" //test", true, 100, Indent::new(0, 100),
&config));
assert_eq!("// comment\n// on a", rewrite_comment("// comment on a", false, 10,
Indent::empty(), &config));
assert_eq!("// A multi line comment\n // between args.",
rewrite_comment("// A multi line comment\n // between args.",
false,
60,
12));
Indent::new(0, 12),
&config));
let input = "// comment";
let expected =
"/* com\n \
* men\n \
* t */";
assert_eq!(expected, rewrite_comment(input, true, 9, 69));
assert_eq!(expected, rewrite_comment(input, true, 9, Indent::new(0, 69), &config));
assert_eq!("/* trimmed */", rewrite_comment("/* trimmed */", true, 100, 100));
assert_eq!("/* trimmed */", rewrite_comment("/* trimmed */", true, 100,
Indent::new(0, 100), &config));
}
// This is probably intended to be a non-test fn, but it is not used. I'm

View File

@ -246,6 +246,7 @@ pub fn get_docs() -> Vec<ConfigHelpItem> {
format_strings: bool, "Format string literals, or leave as is",
chains_overflow_last: bool, "Allow last call in method chain to break the line",
take_source_hints: bool, "Retain some formatting characteristics from the source code",
hard_tabs: bool, "Use tab characters for indentation, spaces for alignment",
}
impl Default for Config {
@ -279,6 +280,7 @@ fn default() -> Config {
format_strings: true,
chains_overflow_last: true,
take_source_hints: true,
hard_tabs: false,
}
}
}

View File

@ -11,11 +11,11 @@
use std::cmp::Ordering;
use std::borrow::Borrow;
use Indent;
use rewrite::{Rewrite, RewriteContext};
use lists::{write_list, itemize_list, ListFormatting, SeparatorTactic, ListTactic};
use string::{StringFormat, rewrite_string};
use utils::{span_after, make_indent, extra_offset, first_line_width, last_line_width, wrap_str,
binary_search};
use utils::{span_after, extra_offset, first_line_width, last_line_width, wrap_str, binary_search};
use visitor::FmtVisitor;
use config::{StructLitStyle, MultilineStyle};
use comment::{FindUncommented, rewrite_comment, contains_comment};
@ -29,7 +29,7 @@
use syntax::visit::Visitor;
impl Rewrite for ast::Expr {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
match self.node {
ast::Expr_::ExprVec(ref expr_vec) => {
rewrite_array(expr_vec.iter().map(|e| &**e), self.span, context, width, offset)
@ -164,7 +164,7 @@ pub fn rewrite_array<'a, I>(expr_iter: I,
span: Span,
context: &RewriteContext,
width: usize,
offset: usize)
offset: Indent)
-> Option<String>
where I: Iterator<Item = &'a ast::Expr>
{
@ -201,6 +201,7 @@ pub fn rewrite_array<'a, I>(expr_iter: I,
h_width: max_item_width,
v_width: max_item_width,
ends_with_newline: false,
config: context.config,
};
let list_str = try_opt!(write_list(&items, &fmt));
@ -215,7 +216,7 @@ fn rewrite_closure(capture: ast::CaptureClause,
span: Span,
context: &RewriteContext,
width: usize,
offset: usize)
offset: Indent)
-> Option<String> {
let mover = if capture == ast::CaptureClause::CaptureByValue {
"move "
@ -258,6 +259,7 @@ fn rewrite_closure(capture: ast::CaptureClause,
h_width: horizontal_budget,
v_width: budget,
ends_with_newline: false,
config: context.config,
};
let list_str = try_opt!(write_list(&arg_items.collect::<Vec<_>>(), &fmt));
let mut prefix = format!("{}|{}|", mover, list_str);
@ -265,7 +267,7 @@ fn rewrite_closure(capture: ast::CaptureClause,
if !ret_str.is_empty() {
if prefix.contains('\n') {
prefix.push('\n');
prefix.push_str(&make_indent(argument_offset));
prefix.push_str(&argument_offset.to_string(context.config));
} else {
prefix.push(' ');
}
@ -308,18 +310,18 @@ fn rewrite_closure(capture: ast::CaptureClause,
.as_ref()
.and_then(|body_expr| {
if let ast::Expr_::ExprBlock(ref inner) = body_expr.node {
Some(inner.rewrite(&context, 2, 0))
Some(inner.rewrite(&context, 2, Indent::empty()))
} else {
None
}
})
.unwrap_or_else(|| body.rewrite(&context, 2, 0));
.unwrap_or_else(|| body.rewrite(&context, 2, Indent::empty()));
Some(format!("{} {}", prefix, try_opt!(body_rewrite)))
}
impl Rewrite for ast::Block {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
let user_str = context.snippet(self.span);
if user_str == "{}" && width >= 2 {
return Some(user_str);
@ -341,7 +343,8 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti
let prefix = if !trimmed.is_empty() {
// 9 = "unsafe {".len(), 7 = "unsafe ".len()
let budget = try_opt!(width.checked_sub(9));
format!("unsafe {} ", rewrite_comment(trimmed, true, budget, offset + 7))
format!("unsafe {} ",
rewrite_comment(trimmed, true, budget, offset + 7, context.config))
} else {
"unsafe ".to_owned()
};
@ -381,7 +384,7 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti
// FIXME(#18): implement pattern formatting
impl Rewrite for ast::Pat {
fn rewrite(&self, context: &RewriteContext, _: usize, _: usize) -> Option<String> {
fn rewrite(&self, context: &RewriteContext, _: usize, _: Indent) -> Option<String> {
Some(context.snippet(self.span))
}
}
@ -447,7 +450,7 @@ fn new_for(pat: &'a ast::Pat,
}
impl<'a> Rewrite for Loop<'a> {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
let label_string = rewrite_label(self.label);
// 2 = " {".len()
let inner_width = try_opt!(width.checked_sub(self.keyword.len() + 2 + label_string.len()));
@ -483,7 +486,7 @@ fn rewrite_range(context: &RewriteContext,
left: Option<&ast::Expr>,
right: Option<&ast::Expr>,
width: usize,
offset: usize)
offset: Indent)
-> Option<String> {
let left_string = match left {
Some(expr) => {
@ -513,7 +516,7 @@ fn rewrite_if_else(context: &RewriteContext,
else_block_opt: Option<&ast::Expr>,
pat: Option<&ast::Pat>,
width: usize,
offset: usize,
offset: Indent,
allow_single_line: bool)
-> Option<String> {
// 3 = "if ", 2 = " {"
@ -588,11 +591,11 @@ fn single_line_if_else(context: &RewriteContext,
let new_width = try_opt!(width.checked_sub(pat_expr_str.len() + fixed_cost));
let if_expr = if_node.expr.as_ref().unwrap();
let if_str = try_opt!(if_expr.rewrite(context, new_width, 0));
let if_str = try_opt!(if_expr.rewrite(context, new_width, Indent::empty()));
let new_width = try_opt!(new_width.checked_sub(if_str.len()));
let else_expr = else_node.expr.as_ref().unwrap();
let else_str = try_opt!(else_expr.rewrite(context, new_width, 0));
let else_str = try_opt!(else_expr.rewrite(context, new_width, Indent::empty()));
// FIXME: this check shouldn't be necessary. Rewrites should either fail
// or wrap to a newline when the object does not fit the width.
@ -621,7 +624,7 @@ fn rewrite_match(context: &RewriteContext,
cond: &ast::Expr,
arms: &[ast::Arm],
width: usize,
offset: usize)
offset: Indent)
-> Option<String> {
if arms.is_empty() {
return None;
@ -634,7 +637,7 @@ fn rewrite_match(context: &RewriteContext,
let nested_context = context.nested_context();
let arm_indent = nested_context.block_indent + context.overflow_indent;
let arm_indent_str = make_indent(arm_indent);
let arm_indent_str = arm_indent.to_string(context.config);
let open_brace_pos = span_after(mk_sp(cond.span.hi, arm_start_pos(&arms[0])),
"{",
@ -669,7 +672,7 @@ fn rewrite_match(context: &RewriteContext,
result.push_str(&arm_indent_str);
let arm_str = arm.rewrite(&nested_context,
context.config.max_width - arm_indent,
context.config.max_width - arm_indent.width(),
arm_indent);
if let Some(ref arm_str) = arm_str {
result.push_str(arm_str);
@ -684,7 +687,7 @@ fn rewrite_match(context: &RewriteContext,
// match expression, but meh.
result.push('\n');
result.push_str(&make_indent(context.block_indent + context.overflow_indent));
result.push_str(&(context.block_indent + context.overflow_indent).to_string(context.config));
result.push('}');
Some(result)
}
@ -704,9 +707,9 @@ fn arm_end_pos(arm: &ast::Arm) -> BytePos {
// Match arms.
impl Rewrite for ast::Arm {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
let &ast::Arm { ref attrs, ref pats, ref guard, ref body } = self;
let indent_str = make_indent(offset);
let indent_str = offset.to_string(context.config);
// FIXME this is all a bit grotty, would be nice to abstract out the
// treatment of attributes.
@ -734,7 +737,7 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti
.map(|p| {
p.rewrite(context,
pat_budget,
offset + context.config.tab_spaces)
offset.block_indent(context.config))
})
.collect::<Option<Vec<_>>>());
@ -775,7 +778,12 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti
// Where the next text can start.
let mut line_start = last_line_width(&pats_str);
if pats_str.find('\n').is_none() {
line_start += offset;
line_start += offset.width();
}
let mut line_indent = offset + pats_width;
if vertical {
line_indent = line_indent.block_indent(context.config);
}
let comma = if let ast::ExprBlock(_) = body.node {
@ -788,7 +796,7 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti
// 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_indent + 4) {
if first_line_width(body_str) <= budget {
return Some(format!("{}{} => {}{}",
attr_str.trim_left(),
@ -810,7 +818,7 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti
Some(format!("{}{} =>\n{}{},",
attr_str.trim_left(),
pats_str,
make_indent(offset + context.config.tab_spaces),
offset.block_indent(context.config).to_string(context.config),
body_str))
}
}
@ -819,7 +827,7 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti
fn rewrite_guard(context: &RewriteContext,
guard: &Option<ptr::P<ast::Expr>>,
width: usize,
offset: usize,
offset: Indent,
// The amount of space used up on this line for the pattern in
// the arm (excludes offset).
pattern_width: usize)
@ -840,10 +848,10 @@ fn rewrite_guard(context: &RewriteContext,
if overhead < width {
let cond_str = guard.rewrite(context,
width - overhead,
offset + context.config.tab_spaces);
offset.block_indent(context.config));
if let Some(cond_str) = cond_str {
return Some(format!("\n{}if {}",
make_indent(offset + context.config.tab_spaces),
offset.block_indent(context.config).to_string(context.config),
cond_str));
}
}
@ -862,7 +870,7 @@ fn rewrite_pat_expr(context: &RewriteContext,
// *without* trailing space.
connector: &str,
width: usize,
offset: usize)
offset: Indent)
-> Option<String> {
let pat_offset = offset + matcher.len();
let mut result = match pat {
@ -898,9 +906,11 @@ fn rewrite_pat_expr(context: &RewriteContext,
// The expression won't fit on the current line, jump to next.
result.push('\n');
result.push_str(&make_indent(pat_offset));
result.push_str(&pat_offset.to_string(context.config));
let expr_rewrite = expr.rewrite(context, context.config.max_width - pat_offset, pat_offset);
let expr_rewrite = expr.rewrite(context,
context.config.max_width - pat_offset.width(),
pat_offset);
result.push_str(&&try_opt!(expr_rewrite));
Some(result)
@ -909,7 +919,7 @@ fn rewrite_pat_expr(context: &RewriteContext,
fn rewrite_string_lit(context: &RewriteContext,
span: Span,
width: usize,
offset: usize)
offset: Indent)
-> Option<String> {
if !context.config.format_strings {
return Some(context.snippet(span));
@ -923,6 +933,7 @@ fn rewrite_string_lit(context: &RewriteContext,
width: width,
offset: offset,
trim_end: false,
config: context.config,
};
let string_lit = context.snippet(span);
@ -936,7 +947,7 @@ pub fn rewrite_call<R>(context: &RewriteContext,
args: &[ptr::P<ast::Expr>],
span: Span,
width: usize,
offset: usize)
offset: Indent)
-> Option<String>
where R: Rewrite
{
@ -955,7 +966,7 @@ fn rewrite_call_inner<R>(context: &RewriteContext,
args: &[ptr::P<ast::Expr>],
span: Span,
width: usize,
offset: usize)
offset: Indent)
-> Result<String, Ordering>
where R: Rewrite
{
@ -1003,7 +1014,7 @@ fn rewrite_call_inner<R>(context: &RewriteContext,
span.lo,
span.hi);
let fmt = ListFormatting::for_fn(remaining_width, offset);
let fmt = ListFormatting::for_fn(remaining_width, offset, context.config);
let list_str = match write_list(&items.collect::<Vec<_>>(), &fmt) {
Some(str) => str,
None => return Err(Ordering::Less),
@ -1015,9 +1026,9 @@ fn rewrite_call_inner<R>(context: &RewriteContext,
fn rewrite_paren(context: &RewriteContext,
subexpr: &ast::Expr,
width: usize,
offset: usize)
offset: Indent)
-> Option<String> {
debug!("rewrite_paren, width: {}, offset: {}", width, offset);
debug!("rewrite_paren, width: {}, offset: {:?}", width, offset);
// 1 is for opening paren, 2 is for opening+closing, we want to keep the closing
// paren on the same line as the subexpr.
let subexpr_str = subexpr.rewrite(context, try_opt!(width.checked_sub(2)), offset + 1);
@ -1031,9 +1042,9 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
base: Option<&'a ast::Expr>,
span: Span,
width: usize,
offset: usize)
offset: Indent)
-> Option<String> {
debug!("rewrite_struct_lit: width {}, offset {}", width, offset);
debug!("rewrite_struct_lit: width {}, offset {:?}", width, offset);
assert!(!fields.is_empty() || base.is_some());
enum StructLitField<'a> {
@ -1054,8 +1065,8 @@ enum StructLitField<'a> {
StructLitStyle::Block => {
// If we are all on one line, then we'll ignore the indent, and we
// have a smaller budget.
let indent = context.block_indent + context.config.tab_spaces;
let v_budget = context.config.max_width.checked_sub(indent).unwrap_or(0);
let indent = context.block_indent.block_indent(context.config);
let v_budget = context.config.max_width.checked_sub(indent.width()).unwrap_or(0);
(indent, v_budget)
}
};
@ -1121,12 +1132,15 @@ enum StructLitField<'a> {
h_width: h_budget,
v_width: v_budget,
ends_with_newline: false,
config: context.config,
};
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);
let inner_indent = context.block_indent
.block_indent(context.config)
.to_string(context.config);
let outer_indent = context.block_indent.to_string(context.config);
Some(format!("{} {{\n{}{}\n{}}}", path_str, inner_indent, fields_str, outer_indent))
};
@ -1143,7 +1157,7 @@ enum StructLitField<'a> {
fn rewrite_field(context: &RewriteContext,
field: &ast::Field,
width: usize,
offset: usize)
offset: Indent)
-> Option<String> {
let name = &field.ident.node.to_string();
let overhead = name.len() + 2;
@ -1156,9 +1170,9 @@ fn rewrite_tuple_lit(context: &RewriteContext,
items: &[ptr::P<ast::Expr>],
span: Span,
width: usize,
offset: usize)
offset: Indent)
-> Option<String> {
debug!("rewrite_tuple_lit: width: {}, offset: {}", width, offset);
debug!("rewrite_tuple_lit: width: {}, offset: {:?}", width, offset);
let indent = offset + 1;
// In case of length 1, need a trailing comma
if items.len() == 1 {
@ -1173,7 +1187,7 @@ fn rewrite_tuple_lit(context: &RewriteContext,
|item| item.span.lo,
|item| item.span.hi,
|item| {
let inner_width = context.config.max_width - indent - 1;
let inner_width = context.config.max_width - indent.width() - 1;
item.rewrite(context, inner_width, indent)
.unwrap_or(context.snippet(item.span))
},
@ -1181,7 +1195,7 @@ fn rewrite_tuple_lit(context: &RewriteContext,
span.hi - BytePos(1));
let budget = try_opt!(width.checked_sub(2));
let fmt = ListFormatting::for_fn(budget, indent);
let fmt = ListFormatting::for_fn(budget, indent, context.config);
let list_str = try_opt!(write_list(&items.collect::<Vec<_>>(), &fmt));
Some(format!("({})", list_str))
@ -1192,7 +1206,7 @@ fn rewrite_binary_op(context: &RewriteContext,
lhs: &ast::Expr,
rhs: &ast::Expr,
width: usize,
offset: usize)
offset: Indent)
-> Option<String> {
// FIXME: format comments between operands and operator
@ -1233,11 +1247,13 @@ fn rewrite_binary_op(context: &RewriteContext,
// We have to use multiple lines.
// Re-evaluate the lhs because we have more space now:
let budget = try_opt!(context.config.max_width.checked_sub(offset + 1 + operator_str.len()));
let budget = try_opt!(context.config
.max_width
.checked_sub(offset.width() + 1 + operator_str.len()));
Some(format!("{} {}\n{}{}",
try_opt!(lhs.rewrite(context, budget, offset)),
operator_str,
make_indent(offset),
offset.to_string(context.config),
rhs_result))
}
@ -1245,7 +1261,7 @@ fn rewrite_unary_op(context: &RewriteContext,
op: &ast::UnOp,
expr: &ast::Expr,
width: usize,
offset: usize)
offset: Indent)
-> Option<String> {
// For some reason, an UnOp is not spanned like BinOp!
let operator_str = match *op {
@ -1265,7 +1281,7 @@ fn rewrite_assignment(context: &RewriteContext,
rhs: &ast::Expr,
op: Option<&ast::BinOp>,
width: usize,
offset: usize)
offset: Indent)
-> Option<String> {
let operator_str = match op {
Some(op) => context.snippet(op.span),
@ -1285,7 +1301,7 @@ pub fn rewrite_assign_rhs<S: Into<String>>(context: &RewriteContext,
lhs: S,
ex: &ast::Expr,
width: usize,
offset: usize)
offset: Indent)
-> Option<String> {
let mut result = lhs.into();
@ -1301,13 +1317,14 @@ pub fn rewrite_assign_rhs<S: Into<String>>(context: &RewriteContext,
None => {
// Expression did not fit on the same line as the identifier. Retry
// on the next line.
let new_offset = offset + context.config.tab_spaces;
result.push_str(&format!("\n{}", make_indent(new_offset)));
let new_offset = offset.block_indent(context.config);
result.push_str(&format!("\n{}", new_offset.to_string(context.config)));
// FIXME: we probably should related max_width to width instead of config.max_width
// where is the 1 coming from anyway?
let max_width = try_opt!(context.config.max_width.checked_sub(new_offset + 1));
let overflow_context = context.overflow_context(context.config.tab_spaces);
let max_width = try_opt!(context.config.max_width.checked_sub(new_offset.width() + 1));
let rhs_indent = Indent::new(context.config.tab_spaces, 0);
let overflow_context = context.overflow_context(rhs_indent);
let rhs = ex.rewrite(&overflow_context, max_width, new_offset);
result.push_str(&&try_opt!(rhs));

View File

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use Indent;
use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic, ListTactic};
use utils::span_after;
use rewrite::{Rewrite, RewriteContext};
@ -20,7 +21,7 @@
impl Rewrite for ast::ViewPath {
// Returns an empty string when the ViewPath is empty (like foo::bar::{})
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
match self.node {
ast::ViewPath_::ViewPathList(_, ref path_list) if path_list.is_empty() => {
Some(String::new())
@ -68,7 +69,7 @@ fn rewrite_single_use_list(path_str: String, vpi: ast::PathListItem) -> String {
// Pretty prints a multi-item import.
// Assumes that path_list.len() > 0.
pub fn rewrite_use_list(width: usize,
offset: usize,
offset: Indent,
path: &ast::Path,
path_list: &[ast::PathListItem],
span: Span,
@ -105,6 +106,7 @@ pub fn rewrite_use_list(width: usize,
// (loose 1 column (";"))
v_width: remaining_width,
ends_with_newline: false,
config: context.config,
};
let mut items = {

View File

@ -10,8 +10,8 @@
// Formatting top-level items - functions, structs, enums, traits, impls.
use utils::{format_mutability, format_visibility, make_indent, contains_skip, span_after,
end_typaram, wrap_str};
use Indent;
use utils::{format_mutability, format_visibility, contains_skip, span_after, end_typaram, wrap_str};
use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic, ListTactic};
use expr::rewrite_assign_rhs;
use comment::FindUncommented;
@ -34,8 +34,7 @@ pub fn visit_let(&mut self, local: &ast::Local, span: Span) {
if let Some(ref ty) = local.ty {
infix.push_str(": ");
// FIXME: silly width, indent
infix.push_str(&ty.rewrite(&self.get_context(), 1000, 0).unwrap());
infix.push_str(&ty.rewrite(&self.get_context(), 1000, Indent::empty()).unwrap());
}
if local.init.is_some() {
@ -52,12 +51,13 @@ pub fn visit_let(&mut self, local: &ast::Local, span: Span) {
let mut result = "let ".to_owned();
let pattern_offset = self.block_indent + result.len() + infix.len();
// 1 = ;
let pattern_width = match self.config.max_width.checked_sub(pattern_offset + 1) {
let pattern_width = self.config.max_width.checked_sub(pattern_offset.width() + 1);
let pattern_width = match pattern_width {
Some(width) => width,
None => return,
};
match local.pat.rewrite(&context, pattern_offset, pattern_width) {
match local.pat.rewrite(&context, pattern_width, pattern_offset) {
Some(ref pat_string) => result.push_str(pat_string),
None => return,
}
@ -65,7 +65,8 @@ pub fn visit_let(&mut self, local: &ast::Local, span: Span) {
result.push_str(&infix);
if let Some(ref ex) = local.init {
let max_width = match self.config.max_width.checked_sub(context.block_indent + 1) {
let max_width = self.config.max_width.checked_sub(context.block_indent.width() + 1);
let max_width = match max_width {
Some(width) => width,
None => return,
};
@ -88,7 +89,7 @@ pub fn visit_let(&mut self, local: &ast::Local, span: Span) {
}
pub fn rewrite_fn(&mut self,
indent: usize,
indent: Indent,
ident: ast::Ident,
fd: &ast::FnDecl,
explicit_self: Option<&ast::ExplicitSelf>,
@ -124,7 +125,7 @@ pub fn rewrite_fn(&mut self,
// this.
if newline_brace {
result.push('\n');
result.push_str(&make_indent(indent));
result.push_str(&indent.to_string(self.config));
} else {
result.push(' ');
}
@ -133,7 +134,7 @@ pub fn rewrite_fn(&mut self,
}
pub fn rewrite_required_fn(&mut self,
indent: usize,
indent: Indent,
ident: ast::Ident,
sig: &ast::MethodSig,
span: Span)
@ -160,7 +161,7 @@ pub fn rewrite_required_fn(&mut self,
}
fn rewrite_fn_base(&mut self,
indent: usize,
indent: Indent,
ident: ast::Ident,
fd: &ast::FnDecl,
explicit_self: Option<&ast::ExplicitSelf>,
@ -207,13 +208,15 @@ fn rewrite_fn_base(&mut self,
result.push_str(&generics_str);
let context = self.get_context();
let ret_str = fd.output.rewrite(&context, self.config.max_width - indent, indent).unwrap();
let ret_str = fd.output
.rewrite(&context, self.config.max_width - indent.width(), indent)
.unwrap();
// Args.
let (one_line_budget, multi_line_budget, mut arg_indent) =
self.compute_budgets_for_args(&result, indent, ret_str.len(), newline_brace);
debug!("rewrite_fn: one_line_budget: {}, multi_line_budget: {}, arg_indent: {}",
debug!("rewrite_fn: one_line_budget: {}, multi_line_budget: {}, arg_indent: {:?}",
one_line_budget,
multi_line_budget,
arg_indent);
@ -222,17 +225,17 @@ fn rewrite_fn_base(&mut self,
if one_line_budget <= 0 {
if self.config.fn_args_paren_newline {
result.push('\n');
result.push_str(&make_indent(arg_indent));
result.push_str(&arg_indent.to_string(self.config));
arg_indent = arg_indent + 1; // extra space for `(`
result.push('(');
} else {
result.push_str("(\n");
result.push_str(&make_indent(arg_indent));
result.push_str(&arg_indent.to_string(self.config));
}
} else if self.config.fn_args_layout == StructLitStyle::Block {
arg_indent = indent + self.config.tab_spaces;
arg_indent = indent.block_indent(self.config);
result.push_str("(\n");
result.push_str(&make_indent(arg_indent));
result.push_str(&arg_indent.to_string(self.config));
} else {
result.push('(');
}
@ -266,7 +269,7 @@ fn rewrite_fn_base(&mut self,
// Unless we are formatting args like a block, in which case there
// should always be room for the return type.
if (result.contains("\n") ||
result.len() + indent + ret_str.len() > self.config.max_width) &&
result.len() + indent.width() + ret_str.len() > self.config.max_width) &&
self.config.fn_args_layout != StructLitStyle::Block {
let indent = match self.config.fn_return_indent {
ReturnIndent::WithWhereClause => indent + 4,
@ -277,7 +280,7 @@ fn rewrite_fn_base(&mut self,
};
result.push('\n');
result.push_str(&make_indent(indent));
result.push_str(&indent.to_string(self.config));
} else {
result.push(' ');
}
@ -326,8 +329,8 @@ fn rewrite_args(&self,
explicit_self: Option<&ast::ExplicitSelf>,
one_line_budget: usize,
multi_line_budget: usize,
indent: usize,
arg_indent: usize,
indent: Indent,
arg_indent: Indent,
span: Span)
-> Option<String> {
let context = self.get_context();
@ -386,7 +389,7 @@ fn rewrite_args(&self,
let indent = match self.config.fn_arg_indent {
BlockIndentStyle::Inherit => indent,
BlockIndentStyle::Tabbed => indent + self.config.tab_spaces,
BlockIndentStyle::Tabbed => indent.block_indent(self.config),
BlockIndentStyle::Visual => arg_indent,
};
@ -398,6 +401,7 @@ fn rewrite_args(&self,
h_width: one_line_budget,
v_width: multi_line_budget,
ends_with_newline: false,
config: self.config,
};
write_list(&arg_items, &fmt)
@ -405,16 +409,16 @@ fn rewrite_args(&self,
fn compute_budgets_for_args(&self,
result: &str,
indent: usize,
indent: Indent,
ret_str_len: usize,
newline_brace: bool)
-> (usize, usize, usize) {
-> (usize, usize, Indent) {
let mut budgets = None;
// Try keeping everything on the same line
if !result.contains("\n") {
// 3 = `() `, space is before ret_string
let mut used_space = indent + result.len() + ret_str_len + 3;
let mut used_space = indent.width() + result.len() + ret_str_len + 3;
if !newline_brace {
used_space += 2;
}
@ -425,7 +429,7 @@ fn compute_budgets_for_args(&self,
};
// 2 = `()`
let used_space = indent + result.len() + 2;
let used_space = indent.width() + result.len() + 2;
let max_space = self.config.ideal_width + self.config.leeway;
debug!("compute_budgets_for_args: used_space: {}, max_space: {}",
used_space,
@ -439,8 +443,8 @@ fn compute_budgets_for_args(&self,
// Didn't work. we must force vertical layout and put args on a newline.
if let None = budgets {
let new_indent = indent + self.config.tab_spaces;
let used_space = new_indent + 2; // account for `(` and `)`
let new_indent = indent.block_indent(self.config);
let used_space = new_indent.width() + 2; // account for `(` and `)`
let max_space = self.config.ideal_width + self.config.leeway;
if used_space > max_space {
// Whoops! bankrupt.
@ -475,13 +479,13 @@ pub fn visit_enum(&mut self,
let generics_str = self.format_generics(generics,
" {",
self.block_indent,
self.block_indent + self.config.tab_spaces,
self.block_indent.block_indent(self.config),
codemap::mk_sp(span.lo, body_start))
.unwrap();
self.buffer.push_str(&generics_str);
self.last_pos = body_start;
self.block_indent += self.config.tab_spaces;
self.block_indent = self.block_indent.block_indent(self.config);
for (i, f) in enum_def.variants.iter().enumerate() {
let next_span_start: BytePos = if i == enum_def.variants.len() - 1 {
span.hi
@ -491,7 +495,7 @@ pub fn visit_enum(&mut self,
self.visit_variant(f, i == enum_def.variants.len() - 1, next_span_start);
}
self.block_indent -= self.config.tab_spaces;
self.block_indent = self.block_indent.block_unindent(self.config);
self.format_missing_with_indent(span.lo + BytePos(enum_snippet.rfind('}').unwrap() as u32));
self.buffer.push_str("}");
@ -521,7 +525,9 @@ fn visit_variant(&mut self, field: &ast::Variant, last_field: bool, next_span_st
|arg| {
// FIXME silly width, indent
arg.ty
.rewrite(&self.get_context(), 1000, 0)
.rewrite(&self.get_context(),
1000,
Indent::empty())
.unwrap()
},
span_after(field.span, "(", self.codemap),
@ -536,7 +542,7 @@ fn visit_variant(&mut self, field: &ast::Variant, last_field: bool, next_span_st
} else {
0
};
let budget = self.config.ideal_width - indent - comma_cost - 1; // 1 = )
let budget = self.config.ideal_width - indent.width() - comma_cost - 1; // 1 = )
let fmt = ListFormatting {
tactic: ListTactic::HorizontalVertical,
@ -546,6 +552,7 @@ fn visit_variant(&mut self, field: &ast::Variant, last_field: bool, next_span_st
h_width: budget,
v_width: budget,
ends_with_newline: true,
config: self.config,
};
let list_str = match write_list(&items.collect::<Vec<_>>(), &fmt) {
Some(list_str) => list_str,
@ -601,7 +608,7 @@ fn format_struct(&self,
struct_def: &ast::StructDef,
generics: Option<&ast::Generics>,
span: Span,
offset: usize)
offset: Indent)
-> Option<String> {
let mut result = String::with_capacity(1024);
@ -654,7 +661,7 @@ fn format_struct(&self,
span.hi);
// 2 terminators and a semicolon
let used_budget = offset + header_str.len() + generics_str.len() + 3;
let used_budget = offset.width() + header_str.len() + generics_str.len() + 3;
// Conservative approximation
let single_line_cost = (span.hi - struct_def.fields[0].span.lo).0;
@ -662,7 +669,7 @@ fn format_struct(&self,
single_line_cost as usize + used_budget > self.config.max_width;
let tactic = if break_line {
let indentation = make_indent(offset + self.config.tab_spaces);
let indentation = offset.block_indent(self.config).to_string(self.config);
result.push('\n');
result.push_str(&indentation);
@ -672,15 +679,16 @@ fn format_struct(&self,
};
// 1 = ,
let budget = self.config.ideal_width - offset + self.config.tab_spaces - 1;
let budget = self.config.ideal_width - offset.width() + self.config.tab_spaces - 1;
let fmt = ListFormatting {
tactic: tactic,
separator: ",",
trailing_separator: self.config.struct_trailing_comma,
indent: offset + self.config.tab_spaces,
indent: offset.block_indent(self.config),
h_width: self.config.max_width,
v_width: budget,
ends_with_newline: true,
config: self.config,
};
let list_str = write_list(&items.collect::<Vec<_>>(), &fmt).unwrap();
@ -688,7 +696,7 @@ fn format_struct(&self,
if break_line {
result.push('\n');
result.push_str(&make_indent(offset));
result.push_str(&offset.to_string(self.config));
}
result.push_str(terminator);
@ -727,8 +735,8 @@ fn format_header(&self, item_name: &str, ident: ast::Ident, vis: ast::Visibility
fn format_generics(&self,
generics: &ast::Generics,
opener: &str,
offset: usize,
generics_offset: usize,
offset: Indent,
generics_offset: Indent,
span: Span)
-> Option<String> {
let mut result = try_opt!(self.rewrite_generics(generics, offset, generics_offset, span));
@ -740,7 +748,7 @@ fn format_generics(&self,
Density::Tall,
span.hi));
result.push_str(&where_clause_str);
result.push_str(&make_indent(self.block_indent));
result.push_str(&self.block_indent.to_string(self.config));
result.push('\n');
result.push_str(opener.trim());
} else {
@ -765,18 +773,18 @@ fn format_field(&self, field: &ast::StructField) -> String {
ast::StructFieldKind::UnnamedField(vis) => format_visibility(vis),
};
// FIXME silly width, indent
let typ = field.node.ty.rewrite(&self.get_context(), 1000, 0).unwrap();
let typ = field.node.ty.rewrite(&self.get_context(), 1000, Indent::empty()).unwrap();
let indent = self.block_indent + self.config.tab_spaces;
let indent = self.block_indent.block_indent(self.config);
let mut attr_str = field.node
.attrs
.rewrite(&self.get_context(),
self.config.max_width - indent,
self.config.max_width - indent.width(),
indent)
.unwrap();
if !attr_str.is_empty() {
attr_str.push('\n');
attr_str.push_str(&make_indent(indent));
attr_str.push_str(&indent.to_string(self.config));
}
match name {
@ -787,8 +795,8 @@ fn format_field(&self, field: &ast::StructField) -> String {
fn rewrite_generics(&self,
generics: &ast::Generics,
offset: usize,
generics_offset: usize,
offset: Indent,
generics_offset: Indent,
span: Span)
-> Option<String> {
// FIXME: convert bounds to where clauses where they get too big or if
@ -801,12 +809,12 @@ fn rewrite_generics(&self,
let offset = match self.config.generics_indent {
BlockIndentStyle::Inherit => offset,
BlockIndentStyle::Tabbed => offset + self.config.tab_spaces,
BlockIndentStyle::Tabbed => offset.block_indent(self.config),
// 1 = <
BlockIndentStyle::Visual => generics_offset + 1,
};
let h_budget = self.config.max_width - generics_offset - 2;
let h_budget = self.config.max_width - generics_offset.width() - 2;
// TODO: might need to insert a newline if the generics are really long.
// Strings for the generics.
@ -841,7 +849,7 @@ fn rewrite_generics(&self,
item.item = ty;
}
let fmt = ListFormatting::for_fn(h_budget, offset);
let fmt = ListFormatting::for_fn(h_budget, offset, self.config);
let list_str = try_opt!(write_list(&items, &fmt));
Some(format!("<{}>", list_str))
@ -850,7 +858,7 @@ fn rewrite_generics(&self,
fn rewrite_where_clause(&self,
where_clause: &ast::WhereClause,
config: &Config,
indent: usize,
indent: Indent,
density: Density,
span_end: BytePos)
-> Option<String> {
@ -859,22 +867,23 @@ fn rewrite_where_clause(&self,
}
let extra_indent = match self.config.where_indent {
BlockIndentStyle::Inherit => 0,
BlockIndentStyle::Tabbed | BlockIndentStyle::Visual => config.tab_spaces,
BlockIndentStyle::Inherit => Indent::empty(),
BlockIndentStyle::Tabbed | BlockIndentStyle::Visual => Indent::new(config.tab_spaces,
0),
};
let context = self.get_context();
let offset = match self.config.where_pred_indent {
BlockIndentStyle::Inherit => indent + extra_indent,
BlockIndentStyle::Tabbed => indent + extra_indent + config.tab_spaces,
BlockIndentStyle::Tabbed => indent + extra_indent.block_indent(config),
// 6 = "where ".len()
BlockIndentStyle::Visual => indent + extra_indent + 6,
};
// FIXME: if where_pred_indent != Visual, then the budgets below might
// be out by a char or two.
let budget = self.config.ideal_width + self.config.leeway - offset;
let budget = self.config.ideal_width + self.config.leeway - offset.width();
let span_start = span_for_where_pred(&where_clause.predicates[0]).lo;
let items = itemize_list(self.codemap,
where_clause.predicates.iter(),
@ -896,13 +905,14 @@ fn rewrite_where_clause(&self,
h_width: budget,
v_width: budget,
ends_with_newline: true,
config: self.config,
};
let preds_str = try_opt!(write_list(&items.collect::<Vec<_>>(), &fmt));
// 9 = " where ".len() + " {".len()
if density == Density::Tall || preds_str.contains('\n') ||
indent + 9 + preds_str.len() > self.config.max_width {
Some(format!("\n{}where {}", make_indent(indent + extra_indent), preds_str))
indent.width() + 9 + preds_str.len() > self.config.max_width {
Some(format!("\n{}where {}", (indent + extra_indent).to_string(self.config), preds_str))
} else {
Some(format!(" where {}", preds_str))
}
@ -910,7 +920,7 @@ fn rewrite_where_clause(&self,
}
impl Rewrite for ast::FunctionRetTy {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
match *self {
ast::FunctionRetTy::DefaultReturn(_) => Some(String::new()),
ast::FunctionRetTy::NoReturn(_) => {
@ -929,7 +939,7 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti
}
impl Rewrite for ast::Arg {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
if is_named_arg(self) {
if let ast::Ty_::TyInfer = self.ty.node {
wrap_str(pprust::pat_to_string(&self.pat), context.config.max_width, width, offset)

View File

@ -44,6 +44,7 @@
use syntax::codemap::CodeMap;
use syntax::diagnostics;
use std::ops::{Add, Sub};
use std::path::PathBuf;
use std::collections::HashMap;
use std::fmt;
@ -80,6 +81,83 @@
// When we get scoped annotations, we should have rustfmt::skip.
const SKIP_ANNOTATION: &'static str = "rustfmt_skip";
#[derive(Copy, Clone, Debug)]
pub struct Indent {
// Width of the block indent, in characters. Must be a multiple of
// Config::tab_spaces.
block_indent: usize,
// Alignment in characters.
alignment: usize,
}
impl Indent {
pub fn new(block_indent: usize, alignment: usize) -> Indent {
Indent { block_indent: block_indent, alignment: alignment }
}
pub fn empty() -> Indent {
Indent::new(0, 0)
}
pub fn block_indent(mut self, config: &Config) -> Indent {
self.block_indent += config.tab_spaces;
self
}
pub fn block_unindent(mut self, config: &Config) -> Indent {
self.block_indent -= config.tab_spaces;
self
}
pub fn width(&self) -> usize {
self.block_indent + self.alignment
}
pub fn to_string(&self, config: &Config) -> String {
let (num_tabs, num_spaces) = if config.hard_tabs {
(self.block_indent / config.tab_spaces, self.alignment)
} else {
(0, self.block_indent + self.alignment)
};
let num_chars = num_tabs + num_spaces;
let mut indent = String::with_capacity(num_chars);
for _ in 0..num_tabs {
indent.push('\t')
}
for _ in 0..num_spaces {
indent.push(' ')
}
indent
}
}
impl Add for Indent {
type Output = Indent;
fn add(self, rhs: Indent) -> Indent {
Indent {
block_indent: self.block_indent + rhs.block_indent,
alignment: self.alignment + rhs.alignment,
}
}
}
impl Sub for Indent {
type Output = Indent;
fn sub(self, rhs: Indent) -> Indent {
Indent::new(self.block_indent - rhs.block_indent, self.alignment - rhs.alignment)
}
}
impl Add<usize> for Indent {
type Output = Indent;
fn add(self, rhs: usize) -> Indent {
Indent::new(self.block_indent, self.alignment + rhs)
}
}
#[derive(Copy, Clone)]
pub enum WriteMode {
// Backups the original file and overwrites the orignal.

View File

@ -13,8 +13,10 @@
use syntax::codemap::{self, CodeMap, BytePos};
use utils::{round_up_to_power_of_two, make_indent, wrap_str};
use Indent;
use utils::{round_up_to_power_of_two, wrap_str};
use comment::{FindUncommented, rewrite_comment, find_comment_end};
use config::Config;
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub enum ListTactic {
@ -44,7 +46,7 @@ pub struct ListFormatting<'a> {
pub tactic: ListTactic,
pub separator: &'a str,
pub trailing_separator: SeparatorTactic,
pub indent: usize,
pub indent: Indent,
// Available width if we layout horizontally.
pub h_width: usize,
// Available width if we layout vertically
@ -52,10 +54,11 @@ pub struct ListFormatting<'a> {
// Non-expressions, e.g. items, will have a new line at the end of the list.
// Important for comment styles.
pub ends_with_newline: bool,
pub config: &'a Config,
}
impl<'a> ListFormatting<'a> {
pub fn for_fn(width: usize, offset: usize) -> ListFormatting<'a> {
pub fn for_fn(width: usize, offset: Indent, config: &'a Config) -> ListFormatting<'a> {
ListFormatting {
tactic: ListTactic::HorizontalVertical,
separator: ",",
@ -64,6 +67,7 @@ pub fn for_fn(width: usize, offset: usize) -> ListFormatting<'a> {
h_width: width,
v_width: width,
ends_with_newline: false,
config: config,
}
}
}
@ -146,12 +150,12 @@ pub fn write_list<'b>(items: &[ListItem], formatting: &ListFormatting<'b>) -> Op
let alloc_width = if tactic == ListTactic::Horizontal {
total_width + total_sep_len
} else {
total_width + items.len() * (formatting.indent + 1)
total_width + items.len() * (formatting.indent.width() + 1)
};
let mut result = String::with_capacity(round_up_to_power_of_two(alloc_width));
let mut line_len = 0;
let indent_str = &make_indent(formatting.indent);
let indent_str = &formatting.indent.to_string(formatting.config);
for (i, item) in items.iter().enumerate() {
let first = i == 0;
let last = i == items.len() - 1;
@ -196,7 +200,8 @@ pub fn write_list<'b>(items: &[ListItem], formatting: &ListFormatting<'b>) -> Op
let block_mode = tactic != ListTactic::Vertical;
// Width restriction is only relevant in vertical mode.
let max_width = formatting.v_width;
result.push_str(&rewrite_comment(comment, block_mode, max_width, formatting.indent));
result.push_str(&rewrite_comment(comment, block_mode, max_width, formatting.indent,
formatting.config));
if tactic == ListTactic::Vertical {
result.push('\n');
@ -206,14 +211,18 @@ pub fn write_list<'b>(items: &[ListItem], formatting: &ListFormatting<'b>) -> Op
}
}
let max_width = formatting.indent + formatting.v_width;
let max_width = formatting.indent.width() + formatting.v_width;
let item_str = wrap_str(&item.item[..], max_width, formatting.v_width, formatting.indent);
result.push_str(&&try_opt!(item_str));
// Post-comments
if tactic != ListTactic::Vertical && item.post_comment.is_some() {
let comment = item.post_comment.as_ref().unwrap();
let formatted_comment = rewrite_comment(comment, true, formatting.v_width, 0);
let formatted_comment = rewrite_comment(comment,
true,
formatting.v_width,
Indent::empty(),
formatting.config);
result.push(' ');
result.push_str(&formatted_comment);
@ -226,14 +235,19 @@ pub fn write_list<'b>(items: &[ListItem], formatting: &ListFormatting<'b>) -> Op
if tactic == ListTactic::Vertical && item.post_comment.is_some() {
// 1 = space between item and comment.
let width = formatting.v_width.checked_sub(item_width + 1).unwrap_or(1);
let offset = formatting.indent + item_width + 1;
let mut offset = formatting.indent;
offset.alignment += item_width + 1;
let comment = item.post_comment.as_ref().unwrap();
// Use block-style only for the last item or multiline comments.
let block_style = !formatting.ends_with_newline && last ||
comment.trim().contains('\n') ||
comment.trim().len() > width;
let formatted_comment = rewrite_comment(comment, block_style, width, offset);
let formatted_comment = rewrite_comment(comment,
block_style,
width,
offset,
formatting.config);
result.push(' ');
result.push_str(&formatted_comment);

View File

@ -25,6 +25,7 @@
use syntax::parse::token::{Eof, Comma, Token};
use syntax::parse::{ParseSess, tts_to_parser};
use Indent;
use rewrite::RewriteContext;
use expr::{rewrite_call, rewrite_array};
use comment::FindUncommented;
@ -46,7 +47,7 @@ enum MacroStyle {
pub fn rewrite_macro(mac: &ast::Mac,
context: &RewriteContext,
width: usize,
offset: usize)
offset: Indent)
-> Option<String> {
let ast::Mac_::MacInvocTT(ref path, ref tt_vec, _) = mac.node;
let style = macro_style(mac, context);

View File

@ -8,7 +8,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use utils::make_indent;
use visitor::FmtVisitor;
use syntax::codemap::{self, BytePos};
@ -21,14 +20,15 @@ pub fn format_missing(&mut self, end: BytePos) {
}
pub fn format_missing_with_indent(&mut self, end: BytePos) {
let config = self.config;
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.
// No new lines in the snippet.
this.buffer.push_str("\n");
}
let indent = make_indent(this.block_indent);
let indent = this.block_indent.to_string(config);
this.buffer.push_str(&indent);
})
}

View File

@ -12,6 +12,7 @@
use syntax::codemap::{CodeMap, Span};
use Indent;
use config::Config;
pub trait Rewrite {
@ -22,7 +23,7 @@ pub trait Rewrite {
/// `width` is the maximum number of characters on the last line
/// (excluding offset). The width of other lines is not limited by
/// `width`.
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String>;
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String>;
}
pub struct RewriteContext<'a> {
@ -30,12 +31,12 @@ pub struct RewriteContext<'a> {
pub config: &'a Config,
// Indentation due to nesting of blocks.
pub block_indent: usize,
pub block_indent: Indent,
// *Extra* indentation due to overflowing to the next line, e.g.,
// let foo =
// bar();
// The extra 4 spaces when formatting `bar()` is overflow_indent.
pub overflow_indent: usize,
pub overflow_indent: Indent,
}
impl<'a> RewriteContext<'a> {
@ -43,12 +44,12 @@ pub fn nested_context(&self) -> RewriteContext<'a> {
RewriteContext {
codemap: self.codemap,
config: self.config,
block_indent: self.block_indent + self.config.tab_spaces,
block_indent: self.block_indent.block_indent(self.config),
overflow_indent: self.overflow_indent,
}
}
pub fn overflow_context(&self, overflow: usize) -> RewriteContext<'a> {
pub fn overflow_context(&self, overflow: Indent) -> RewriteContext<'a> {
RewriteContext {
codemap: self.codemap,
config: self.config,

View File

@ -13,7 +13,9 @@
use unicode_segmentation::UnicodeSegmentation;
use regex::Regex;
use utils::{make_indent, round_up_to_power_of_two};
use Indent;
use config::Config;
use utils::round_up_to_power_of_two;
use MIN_STRING;
@ -23,8 +25,9 @@ pub struct StringFormat<'a> {
pub line_start: &'a str,
pub line_end: &'a str,
pub width: usize,
pub offset: usize,
pub offset: Indent,
pub trim_end: bool,
pub config: &'a Config,
}
// TODO: simplify this!
@ -36,7 +39,7 @@ pub fn rewrite_string<'a>(s: &str, fmt: &StringFormat<'a>) -> String {
let graphemes = UnicodeSegmentation::graphemes(&*stripped_str, false).collect::<Vec<&str>>();
let indent = make_indent(fmt.offset);
let indent = fmt.offset.to_string(fmt.config);
let indent = &indent;
let mut cur_start = 0;

View File

@ -12,12 +12,13 @@
use syntax::print::pprust;
use syntax::codemap::{self, Span, BytePos, CodeMap};
use Indent;
use lists::{itemize_list, write_list, ListFormatting};
use rewrite::{Rewrite, RewriteContext};
use utils::{extra_offset, span_after};
impl Rewrite for ast::Path {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
rewrite_path(context, None, self, width, offset)
}
}
@ -27,7 +28,7 @@ pub fn rewrite_path(context: &RewriteContext,
qself: Option<&ast::QSelf>,
path: &ast::Path,
width: usize,
offset: usize)
offset: Indent)
-> Option<String> {
let skip_count = qself.map(|x| x.position).unwrap_or(0);
@ -80,7 +81,7 @@ fn rewrite_path_segments<'a, I>(mut buffer: String,
span_hi: BytePos,
context: &RewriteContext,
width: usize,
offset: usize)
offset: Indent)
-> Option<String>
where I: Iterator<Item = &'a ast::PathSegment>
{
@ -128,7 +129,7 @@ fn get_span(&self) -> Span {
impl<'a> Rewrite for SegmentParam<'a> {
// FIXME: doesn't always use width, offset.
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
Some(match *self {
SegmentParam::LifeTime(ref lt) => {
pprust::lifetime_to_string(lt)
@ -186,7 +187,7 @@ fn rewrite_segment(segment: &ast::PathSegment,
span_hi: BytePos,
context: &RewriteContext,
width: usize,
offset: usize)
offset: Indent)
-> Option<String> {
let ident_len = segment.identifier.to_string().len();
let width = try_opt!(width.checked_sub(ident_len));
@ -229,7 +230,7 @@ fn rewrite_segment(segment: &ast::PathSegment,
list_lo,
span_hi);
let fmt = ListFormatting::for_fn(list_width, offset + extra_offset);
let fmt = ListFormatting::for_fn(list_width, offset + extra_offset, context.config);
let list_str = try_opt!(write_list(&items.collect::<Vec<_>>(), &fmt));
// Update position of last bracket.
@ -257,7 +258,7 @@ fn rewrite_segment(segment: &ast::PathSegment,
let budget = try_opt!(width.checked_sub(output.len() + 2));
// 1 for (
let fmt = ListFormatting::for_fn(budget, offset + 1);
let fmt = ListFormatting::for_fn(budget, offset + 1, context.config);
let list_str = try_opt!(write_list(&items.collect::<Vec<_>>(), &fmt));
format!("({}){}", list_str, output)
@ -269,7 +270,7 @@ fn rewrite_segment(segment: &ast::PathSegment,
}
impl Rewrite for ast::WherePredicate {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
// TODO: dead spans?
// TODO: don't assume we'll always fit on one line...
Some(match *self {
@ -340,7 +341,7 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti
}
impl Rewrite for ast::LifetimeDef {
fn rewrite(&self, _: &RewriteContext, _: usize, _: usize) -> Option<String> {
fn rewrite(&self, _: &RewriteContext, _: usize, _: Indent) -> Option<String> {
if self.bounds.is_empty() {
Some(pprust::lifetime_to_string(&self.lifetime))
} else {
@ -356,7 +357,7 @@ fn rewrite(&self, _: &RewriteContext, _: usize, _: usize) -> Option<String> {
}
impl Rewrite for ast::TyParamBound {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
match *self {
ast::TyParamBound::TraitTyParamBound(ref tref, ast::TraitBoundModifier::None) => {
tref.rewrite(context, width, offset)
@ -372,7 +373,7 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti
}
impl Rewrite for ast::TyParamBounds {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
let strs: Vec<_> = self.iter()
.map(|b| b.rewrite(context, width, offset).unwrap())
.collect();
@ -382,7 +383,7 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti
// FIXME: this assumes everything will fit on one line
impl Rewrite for ast::TyParam {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
let mut result = String::with_capacity(128);
result.push_str(&self.ident.to_string());
if !self.bounds.is_empty() {
@ -407,7 +408,7 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti
// FIXME: this assumes everything will fit on one line
impl Rewrite for ast::PolyTraitRef {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
if !self.bound_lifetimes.is_empty() {
let lifetime_str = self.bound_lifetimes
.iter()
@ -430,7 +431,7 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti
impl Rewrite for ast::Ty {
// FIXME doesn't always use width, offset
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
match self.node {
ast::TyPath(None, ref p) => {
p.rewrite(context, width, offset)

View File

@ -13,6 +13,7 @@
use syntax::ast::{self, Visibility, Attribute, MetaItem, MetaItem_};
use syntax::codemap::{CodeMap, Span, BytePos};
use Indent;
use comment::FindUncommented;
use rewrite::{Rewrite, RewriteContext};
@ -20,10 +21,10 @@
// Computes the length of a string's last line, minus offset.
#[inline]
pub fn extra_offset(text: &str, offset: usize) -> usize {
pub fn extra_offset(text: &str, offset: Indent) -> usize {
match text.rfind('\n') {
// 1 for newline character
Some(idx) => text.len() - idx - 1 - offset,
Some(idx) => text.len() - idx - 1 - offset.width(),
None => text.len(),
}
}
@ -35,15 +36,6 @@ pub fn span_after(original: Span, needle: &str, codemap: &CodeMap) -> BytePos {
original.lo + BytePos(snippet.find_uncommented(needle).unwrap() as u32 + 1)
}
#[inline]
pub fn make_indent(width: usize) -> String {
let mut indent = String::with_capacity(width);
for _ in 0..width {
indent.push(' ')
}
indent
}
#[inline]
pub fn format_visibility(vis: Visibility) -> &'static str {
match vis {
@ -186,7 +178,7 @@ macro_rules! try_opt {
// Wraps string-like values in an Option. Returns Some when the string adheres
// to the Rewrite constraints defined for the Rewrite trait and else otherwise.
pub fn wrap_str<S: AsRef<str>>(s: S, max_width: usize, width: usize, offset: usize) -> Option<S> {
pub fn wrap_str<S: AsRef<str>>(s: S, max_width: usize, width: usize, offset: Indent) -> Option<S> {
{
let snippet = s.as_ref();
@ -197,7 +189,7 @@ pub fn wrap_str<S: AsRef<str>>(s: S, max_width: usize, width: usize, offset: usi
// The caller of this function has already placed `offset`
// characters on the first line.
let first_line_max_len = try_opt!(max_width.checked_sub(offset));
let first_line_max_len = try_opt!(max_width.checked_sub(offset.width()));
if lines.next().unwrap().len() > first_line_max_len {
return None;
}
@ -211,7 +203,7 @@ pub fn wrap_str<S: AsRef<str>>(s: S, max_width: usize, width: usize, offset: usi
// indentation.
// A special check for the last line, since the caller may
// place trailing characters on this line.
if snippet.lines().rev().next().unwrap().len() > offset + width {
if snippet.lines().rev().next().unwrap().len() > offset.width() + width {
return None;
}
}
@ -221,7 +213,7 @@ pub fn wrap_str<S: AsRef<str>>(s: S, max_width: usize, width: usize, offset: usi
}
impl Rewrite for String {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
wrap_str(self, context.config.max_width, width, offset).map(ToOwned::to_owned)
}
}

View File

@ -14,6 +14,7 @@
use strings::string_buffer::StringBuffer;
use Indent;
use utils;
use config::Config;
use rewrite::{Rewrite, RewriteContext};
@ -25,7 +26,7 @@ pub struct FmtVisitor<'a> {
pub buffer: StringBuffer,
pub last_pos: BytePos,
// TODO: RAII util for indenting
pub block_indent: usize,
pub block_indent: Indent,
pub config: &'a Config,
}
@ -38,8 +39,9 @@ fn visit_expr(&mut self, ex: &'v ast::Expr) {
self.codemap.lookup_char_pos(ex.span.hi));
self.format_missing(ex.span.lo);
let offset = self.buffer.cur_offset();
let rewrite = ex.rewrite(&self.get_context(), self.config.max_width - offset, offset);
let rewrite = ex.rewrite(&self.get_context(),
self.config.max_width - self.block_indent.width(),
self.block_indent);
if let Some(new_str) = rewrite {
self.buffer.push_str(&new_str);
@ -65,7 +67,8 @@ fn visit_stmt(&mut self, stmt: &'v ast::Stmt) {
// 1 = trailing semicolon;
let rewrite = ex.rewrite(&self.get_context(),
self.config.max_width - self.block_indent - suffix.len(),
self.config.max_width - self.block_indent.width() -
suffix.len(),
self.block_indent);
if let Some(new_str) = rewrite {
@ -96,7 +99,7 @@ fn visit_block(&mut self, b: &'v ast::Block) {
};
self.last_pos = self.last_pos + brace_compensation;
self.block_indent += self.config.tab_spaces;
self.block_indent = self.block_indent.block_indent(self.config);
self.buffer.push_str("{");
for stmt in &b.stmts {
@ -111,8 +114,8 @@ fn visit_block(&mut self, b: &'v ast::Block) {
None => {}
}
self.block_indent -= self.config.tab_spaces;
// TODO: we should compress any newlines here to just one.
self.block_indent = self.block_indent.block_unindent(self.config);
// TODO: we should compress any newlines here to just one
self.format_missing_with_indent(b.span.hi - brace_compensation);
self.buffer.push_str("}");
self.last_pos = b.span.hi;
@ -126,6 +129,7 @@ fn visit_fn(&mut self,
b: &'v ast::Block,
s: Span,
_: ast::NodeId) {
let indent = self.block_indent;
let rewrite = match fk {
visit::FnKind::ItemFn(ident,
@ -191,9 +195,9 @@ fn visit_item(&mut self, item: &'v ast::Item) {
}
ast::Item_::ItemImpl(..) |
ast::Item_::ItemTrait(..) => {
self.block_indent += self.config.tab_spaces;
self.block_indent = self.block_indent.block_indent(self.config);
visit::walk_item(self, item);
self.block_indent -= self.config.tab_spaces;
self.block_indent = self.block_indent.block_unindent(self.config);
}
ast::Item_::ItemExternCrate(_) => {
self.format_missing_with_indent(item.span.lo);
@ -256,7 +260,7 @@ fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) {
fn visit_mac(&mut self, mac: &'v ast::Mac) {
// 1 = ;
let width = self.config.max_width - self.block_indent - 1;
let width = self.config.max_width - self.block_indent.width() - 1;
let rewrite = rewrite_macro(mac, &self.get_context(), width, self.block_indent);
if let Some(res) = rewrite {
@ -272,7 +276,7 @@ pub fn from_codemap<'b>(codemap: &'b CodeMap, config: &'b Config) -> FmtVisitor<
codemap: codemap,
buffer: StringBuffer::new(),
last_pos: BytePos(0),
block_indent: 0,
block_indent: Indent { block_indent: 0, alignment: 0 },
config: config,
}
}
@ -302,7 +306,7 @@ pub fn visit_attrs(&mut self, attrs: &[ast::Attribute]) -> bool {
true
} else {
let rewrite = attrs.rewrite(&self.get_context(),
self.config.max_width - self.block_indent,
self.config.max_width - self.block_indent.width(),
self.block_indent)
.unwrap();
self.buffer.push_str(&rewrite);
@ -323,32 +327,33 @@ fn format_mod(&mut self, m: &ast::Mod, s: Span, ident: ast::Ident) {
if is_internal {
debug!("FmtVisitor::format_mod: internal mod");
self.block_indent += self.config.tab_spaces;
self.block_indent = self.block_indent.block_indent(self.config);
visit::walk_mod(self, m);
debug!("... last_pos after: {:?}", self.last_pos);
self.block_indent -= self.config.tab_spaces;
self.block_indent = self.block_indent.block_unindent(self.config);
}
}
pub fn format_separate_mod(&mut self, m: &ast::Mod, filename: &str) {
let filemap = self.codemap.get_filemap(filename);
self.last_pos = filemap.start_pos;
self.block_indent = 0;
self.block_indent = Indent::empty();
visit::walk_mod(self, m);
self.format_missing(filemap.end_pos);
}
fn format_import(&mut self, vis: ast::Visibility, vp: &ast::ViewPath, span: Span) {
let vis = utils::format_visibility(vis);
let offset = self.block_indent + vis.len() + "use ".len();
let mut offset = self.block_indent;
offset.alignment += vis.len() + "use ".len();
let context = RewriteContext {
codemap: self.codemap,
config: self.config,
block_indent: self.block_indent,
overflow_indent: 0,
overflow_indent: Indent::empty(),
};
// 1 = ";"
match vp.rewrite(&context, self.config.max_width - offset - 1, offset) {
match vp.rewrite(&context, self.config.max_width - offset.width() - 1, offset) {
Some(ref s) if s.is_empty() => {
// Format up to last newline
let prev_span = codemap::mk_sp(self.last_pos, span.lo);
@ -377,18 +382,18 @@ pub fn get_context(&self) -> RewriteContext {
codemap: self.codemap,
config: self.config,
block_indent: self.block_indent,
overflow_indent: 0,
overflow_indent: Indent::empty(),
}
}
}
impl<'a> Rewrite for [ast::Attribute] {
fn rewrite(&self, context: &RewriteContext, _: usize, offset: usize) -> Option<String> {
fn rewrite(&self, context: &RewriteContext, _: usize, offset: Indent) -> Option<String> {
let mut result = String::new();
if self.is_empty() {
return Some(result);
}
let indent = utils::make_indent(offset);
let indent = offset.to_string(context.config);
for (i, a) in self.iter().enumerate() {
let a_str = context.snippet(a.span);
@ -403,8 +408,9 @@ fn rewrite(&self, context: &RewriteContext, _: usize, offset: usize) -> Option<S
if !comment.is_empty() {
let comment = rewrite_comment(comment,
false,
context.config.max_width - offset,
offset);
context.config.max_width - offset.width(),
offset,
context.config);
result.push_str(&indent);
result.push_str(&comment);
result.push('\n');

69
tests/source/hard-tabs.rs Normal file
View File

@ -0,0 +1,69 @@
// rustfmt-hard_tabs: true
fn main() {
let x = Bar;
let y = Foo {a: x };
Foo { a: foo() /* comment*/, /* comment*/ b: bar(), ..something };
fn foo(a: i32, a: i32, a: i32, a: i32, a: i32, a: i32, a: i32, a: i32, a: i32, a: i32, a: i32) {}
let str = "AAAAAAAAAAAAAAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAa";
if let (some_very_large, tuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuple) = 1
+ 2 + 3 {
}
if cond() {
something();
} else if different_cond() {
something_else();
} else {
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
}
unsafe /* very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong comment */ {}
unsafe // So this is a very long comment.
// Multi-line, too.
// Will it still format correctly?
{
}
let z = [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzz, q];
fn generic<T>(arg: T) -> &SomeType
where T: Fn(// First arg
A,
// Second argument
B, C, D, /* pre comment */ E /* last comment */) -> &SomeType {
arg(a, b, c, d, e)
}
loong_func().quux(move || {
if true {
1
} else {
2
}
});
fffffffffffffffffffffffffffffffffff(a,
{
SCRIPT_TASK_ROOT
.with(|root| {
*root.borrow_mut() = Some(&script_task);
});
});
a.b
.c
.d();
x().y(|| {
match cond() {
true => (),
false => (),
}
});
}

95
tests/target/hard-tabs.rs Normal file
View File

@ -0,0 +1,95 @@
// rustfmt-hard_tabs: true
fn main() {
let x = Bar;
let y = Foo { a: x };
Foo {
a: foo(), // comment
// comment
b: bar(),
..something
};
fn foo(a: i32,
a: i32,
a: i32,
a: i32,
a: i32,
a: i32,
a: i32,
a: i32,
a: i32,
a: i32,
a: i32) {
}
let str = "AAAAAAAAAAAAAAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAaAa";
if let (some_very_large, tuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuple) =
1 + 2 + 3 {
}
if cond() {
something();
} else if different_cond() {
something_else();
} else {
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
}
unsafe /* very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong
* comment */ {
}
unsafe /* So this is a very long comment.
* Multi-line, too.
* Will it still format correctly? */ {
}
let z = [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,
yyyyyyyyyyyyyyyyyyyyyyyyyyy,
zzzzzzzzzzzzzzzzzz,
q];
fn generic<T>(arg: T) -> &SomeType
where T: Fn(// First arg
A,
// Second argument
B,
C,
D,
// pre comment
E /* last comment */) -> &SomeType
{
arg(a, b, c, d, e)
}
loong_func().quux(move || {
if true {
1
} else {
2
}
});
fffffffffffffffffffffffffffffffffff(a,
{
SCRIPT_TASK_ROOT.with(|root| {
*root.borrow_mut() = Some(&script_task);
});
});
a.b
.c
.d();
x().y(|| {
match cond() {
true => (),
false => (),
}
});
}