Refactor some things; add extra tests.

This commit is contained in:
Marcus Klaas 2015-07-20 23:29:25 +02:00
parent 2fa6220f57
commit c4101de53d
15 changed files with 292 additions and 88 deletions

View File

@ -14,6 +14,18 @@
use lists::SeparatorTactic; use lists::SeparatorTactic;
use issues::ReportTactic; use issues::ReportTactic;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum BlockIndentStyle {
// Same level as parent.
Inherit,
// One level deeper than parent.
Tabbed,
// Aligned with block open.
Visual,
}
impl_enum_decodable!(BlockIndentStyle, Inherit, Tabbed, Visual);
#[derive(RustcDecodable, Clone)] #[derive(RustcDecodable, Clone)]
pub struct Config { pub struct Config {
pub max_width: usize, pub max_width: usize,
@ -31,6 +43,7 @@ pub struct Config {
pub report_todo: ReportTactic, pub report_todo: ReportTactic,
pub report_fixme: ReportTactic, pub report_fixme: ReportTactic,
pub reorder_imports: bool, // Alphabetically, case sensitive. pub reorder_imports: bool, // Alphabetically, case sensitive.
pub expr_indent_style: BlockIndentStyle,
} }
impl Config { impl Config {

View File

@ -13,3 +13,4 @@ enum_trailing_comma = true
report_todo = "Always" report_todo = "Always"
report_fixme = "Never" report_fixme = "Never"
reorder_imports = false reorder_imports = false
expr_indent_style = "Tabbed"

View File

@ -14,6 +14,7 @@
use StructLitStyle; use StructLitStyle;
use utils::{span_after, make_indent}; use utils::{span_after, make_indent};
use visitor::FmtVisitor; use visitor::FmtVisitor;
use config::BlockIndentStyle;
use syntax::{ast, ptr}; use syntax::{ast, ptr};
use syntax::codemap::{Pos, Span, BytePos, mk_sp}; use syntax::codemap::{Pos, Span, BytePos, mk_sp};
@ -57,48 +58,16 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti
rewrite_tuple_lit(context, items, self.span, width, offset) rewrite_tuple_lit(context, items, self.span, width, offset)
} }
ast::Expr_::ExprWhile(ref cond, ref block, label) => { ast::Expr_::ExprWhile(ref cond, ref block, label) => {
rewrite_loop(context, Loop::new_while(None, cond, block, label).rewrite(context, width, offset)
cond,
block,
label,
None,
"while ",
"let ",
" = ",
width,
offset)
} }
ast::Expr_::ExprWhileLet(ref pat, ref cond, ref block, label) => { ast::Expr_::ExprWhileLet(ref pat, ref cond, ref block, label) => {
rewrite_loop(context, Loop::new_while(Some(pat), cond, block, label).rewrite(context, width, offset)
cond,
block,
label,
Some(pat),
"while ",
"let ",
" = ",
width,
offset)
} }
ast::Expr_::ExprForLoop(ref pat, ref cond, ref block, label) => { ast::Expr_::ExprForLoop(ref pat, ref cond, ref block, label) => {
rewrite_loop(context, Loop::new_for(pat, cond, block, label).rewrite(context, width, offset)
cond,
block,
label,
Some(pat),
"for ",
"",
" in ",
width,
offset)
} }
ast::Expr_::ExprLoop(ref block, label) => { ast::Expr_::ExprLoop(ref block, label) => {
// Of all the loops, this is the only one that does not use Loop::new_loop(block, label).rewrite(context, width, offset)
// rewrite_loop!
// FIXME: this drops any comment between "loop" and the block.
block.rewrite(context, width, offset).map(|result| {
format!("{}loop {}", rewrite_label(label), result)
})
} }
ast::Expr_::ExprBlock(ref block) => { ast::Expr_::ExprBlock(ref block) => {
block.rewrite(context, width, offset) block.rewrite(context, width, offset)
@ -121,7 +90,8 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti
width, width,
offset) offset)
} }
// We reformat it ourselves because rustc gives us a bad span for ranges // We reformat it ourselves because rustc gives us a bad span
// for ranges, see rust#27162
ast::Expr_::ExprRange(ref left, ref right) => { ast::Expr_::ExprRange(ref left, ref right) => {
rewrite_range(context, rewrite_range(context,
left.as_ref().map(|e| &**e), left.as_ref().map(|e| &**e),
@ -161,6 +131,91 @@ fn rewrite(&self, context: &RewriteContext, _: usize, _: usize) -> Option<String
} }
} }
// Abstraction over for, while and loop expressions
struct Loop<'a> {
cond: Option<&'a ast::Expr>,
block: &'a ast::Block,
label: Option<ast::Ident>,
pat: Option<&'a ast::Pat>,
keyword: &'a str,
matcher: &'a str,
connector: &'a str,
}
impl<'a> Loop<'a> {
fn new_loop(block: &'a ast::Block, label: Option<ast::Ident>) -> Loop<'a> {
Loop {
cond: None,
block: block,
label: label,
pat: None,
keyword: "loop",
matcher: "",
connector: "",
}
}
fn new_while(pat: Option<&'a ast::Pat>,
cond: &'a ast::Expr,
block: &'a ast::Block,
label: Option<ast::Ident>)
-> Loop<'a> {
Loop {
cond: Some(cond),
block: block,
label: label,
pat: pat,
keyword: "while ",
matcher: match pat {
Some(..) => "let ",
None => ""
},
connector: " =",
}
}
fn new_for(pat: &'a ast::Pat,
cond: &'a ast::Expr,
block: &'a ast::Block,
label: Option<ast::Ident>)
-> Loop<'a> {
Loop {
cond: Some(cond),
block: block,
label: label,
pat: Some(pat),
keyword: "for ",
matcher: "",
connector: " in",
}
}
}
impl<'a> Rewrite for Loop<'a> {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
let label_string = rewrite_label(self.label);
// 2 = " {".len()
let inner_width = width - self.keyword.len() - 2 - label_string.len();
let inner_offset = offset + self.keyword.len() + label_string.len();
let pat_expr_string = match self.cond {
Some(cond) => try_opt!(rewrite_pat_expr(context,
self.pat,
cond,
self.matcher,
self.connector,
inner_width,
inner_offset)),
None => String::new()
};
// 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)
})
}
}
fn rewrite_label(label: Option<ast::Ident>) -> String { fn rewrite_label(label: Option<ast::Ident>) -> String {
match label { match label {
Some(ident) => format!("{}: ", ident), Some(ident) => format!("{}: ", ident),
@ -193,36 +248,8 @@ fn rewrite_range(context: &RewriteContext,
Some(format!("{}..{}", left_string, right_string)) Some(format!("{}..{}", left_string, right_string))
} }
fn rewrite_loop(context: &RewriteContext, // Rewrites if-else blocks. If let Some(_) = pat, the expression is
cond: &ast::Expr, // treated as an if-let-else expression.
block: &ast::Block,
label: Option<ast::Ident>,
pat: Option<&ast::Pat>,
keyword: &str,
matcher: &str, // FIXME: think of better identifiers
connector: &str,
width: usize,
offset: usize)
-> Option<String> {
let label_string = rewrite_label(label);
// 2 = " {"
let inner_width = width - keyword.len() - 2 - label_string.len();
let inner_offset = offset + keyword.len() + label_string.len();
let pat_expr_string = try_opt!(rewrite_pat_expr(context,
pat,
cond,
matcher,
connector,
inner_width,
inner_offset));
// FIXME: this drops any comment between "loop" and the block.
block.rewrite(context, width, offset).map(|result| {
format!("{}{}{} {}", label_string, keyword, pat_expr_string, result)
})
}
fn rewrite_if_else(context: &RewriteContext, fn rewrite_if_else(context: &RewriteContext,
cond: &ast::Expr, cond: &ast::Expr,
if_block: &ast::Block, if_block: &ast::Block,
@ -261,28 +288,49 @@ fn rewrite_pat_expr(context: &RewriteContext,
width: usize, width: usize,
offset: usize) offset: usize)
-> Option<String> { -> Option<String> {
let pat_offset = offset + matcher.len();
let mut result = match pat { let mut result = match pat {
Some(pat) => { Some(pat) => {
let pat_string = try_opt!(pat.rewrite(context, let pat_string = try_opt!(pat.rewrite(context,
width - connector.len() - matcher.len(), width - connector.len() - matcher.len(),
offset + matcher.len())); pat_offset));
format!("{}{}{}", matcher, pat_string, connector) format!("{}{}{}", matcher, pat_string, connector)
} }
None => String::new() None => String::new()
}; };
// Consider only the last line of the pat string // Consider only the last line of the pat string.
let extra_offset = match result.rfind('\n') { let extra_offset = match result.rfind('\n') {
// 1 for newline character // 1 for newline character
Some(idx) => result.len() - idx - 1 - offset, Some(idx) => result.len() - idx - 1 - offset,
None => result.len() None => result.len()
}; };
let expr_string = try_opt!(expr.rewrite(context, // The expression may (partionally) fit on the current line.
width - extra_offset, if width > extra_offset + 1 {
offset + extra_offset)); let mut corrected_offset = extra_offset;
if pat.is_some() {
result.push(' ');
corrected_offset += 1;
}
let expr_rewrite = expr.rewrite(context,
width - corrected_offset,
offset + corrected_offset);
if let Some(expr_string) = expr_rewrite {
result.push_str(&expr_string); result.push_str(&expr_string);
return Some(result);
}
}
// The expression won't fit on the current line, jump to next.
result.push('\n');
result.push_str(&make_indent(pat_offset));
let expr_rewrite = expr.rewrite(context, context.config.max_width - pat_offset, pat_offset);
result.push_str(&&try_opt!(expr_rewrite));
Some(result) Some(result)
} }
@ -333,6 +381,8 @@ fn rewrite_call(context: &RewriteContext,
// 2 is for parens. // 2 is for parens.
let remaining_width = width - callee_str.len() - 2; let remaining_width = width - callee_str.len() - 2;
let offset = callee_str.len() + 1 + offset; let offset = callee_str.len() + 1 + offset;
let block_indent = expr_block_indent(context, offset);
let inner_context = &RewriteContext { block_indent: block_indent, ..*context };
let items = itemize_list(context.codemap, let items = itemize_list(context.codemap,
Vec::new(), Vec::new(),
@ -342,7 +392,7 @@ fn rewrite_call(context: &RewriteContext,
|item| item.span.lo, |item| item.span.lo,
|item| item.span.hi, |item| item.span.hi,
// Take old span when rewrite fails. // Take old span when rewrite fails.
|item| item.rewrite(context, remaining_width, offset) |item| item.rewrite(inner_context, remaining_width, offset)
.unwrap_or(context.codemap.span_to_snippet(item.span) .unwrap_or(context.codemap.span_to_snippet(item.span)
.unwrap()), .unwrap()),
callee.span.hi + BytePos(1), callee.span.hi + BytePos(1),
@ -361,6 +411,14 @@ fn rewrite_call(context: &RewriteContext,
Some(format!("{}({})", callee_str, write_list(&items, &fmt))) Some(format!("{}({})", callee_str, write_list(&items, &fmt)))
} }
fn expr_block_indent(context: &RewriteContext, offset: usize) -> usize {
match context.config.expr_indent_style {
BlockIndentStyle::Inherit => context.block_indent,
BlockIndentStyle::Tabbed => context.block_indent + context.config.tab_spaces,
BlockIndentStyle::Visual => offset,
}
}
fn rewrite_paren(context: &RewriteContext, fn rewrite_paren(context: &RewriteContext,
subexpr: &ast::Expr, subexpr: &ast::Expr,
width: usize, width: usize,
@ -391,17 +449,18 @@ enum StructLitField<'a> {
} }
let path_str = pprust::path_to_string(path); let path_str = pprust::path_to_string(path);
let (indent, h_budget, v_budget) = match context.config.struct_lit_style {
StructLitStyle::VisualIndent => {
// Foo { a: Foo } - indent is +3, width is -5. // Foo { a: Foo } - indent is +3, width is -5.
let budget = width - (path_str.len() + 5); let h_budget = width.checked_sub(path_str.len() + 5).unwrap_or(0);
(offset + path_str.len() + 3, budget, budget) let (indent, v_budget) = match context.config.struct_lit_style {
StructLitStyle::VisualIndent => {
(offset + path_str.len() + 3, h_budget)
} }
StructLitStyle::BlockIndent => { StructLitStyle::BlockIndent => {
// If we are all on one line, then we'll ignore the indent, and we // If we are all on one line, then we'll ignore the indent, and we
// have a smaller budget. // have a smaller budget.
let indent = context.block_indent + context.config.tab_spaces; let indent = context.block_indent + context.config.tab_spaces;
(indent, width - (path_str.len() + 5), width - indent) let v_budget = context.config.max_width.checked_sub(indent).unwrap_or(0);
(indent, v_budget)
} }
}; };

View File

@ -49,10 +49,9 @@
use visitor::FmtVisitor; use visitor::FmtVisitor;
use config::Config; use config::Config;
#[macro_use]
mod config;
#[macro_use] #[macro_use]
mod utils; mod utils;
pub mod config;
mod changes; mod changes;
mod visitor; mod visitor;
mod items; mod items;

View File

@ -0,0 +1,16 @@
max_width = 100
ideal_width = 80
leeway = 5
tab_spaces = 4
newline_style = "Unix"
fn_brace_style = "SameLineWhere"
fn_return_indent = "WithArgs"
fn_args_paren_newline = true
struct_trailing_comma = "Vertical"
struct_lit_style = "BlockIndent"
struct_lit_trailing_comma = "Vertical"
enum_trailing_comma = true
report_todo = "Always"
report_fixme = "Never"
reorder_imports = false
expr_indent_style = "Visual"

View File

@ -13,3 +13,4 @@ enum_trailing_comma = true
report_todo = "Always" report_todo = "Always"
report_fixme = "Never" report_fixme = "Never"
reorder_imports = true reorder_imports = true
expr_indent_style = "Tabbed"

View File

@ -13,3 +13,4 @@ enum_trailing_comma = true
report_todo = "Always" report_todo = "Always"
report_fixme = "Never" report_fixme = "Never"
reorder_imports = false reorder_imports = false
expr_indent_style = "Tabbed"

View File

@ -13,3 +13,4 @@ enum_trailing_comma = true
report_todo = "Always" report_todo = "Always"
report_fixme = "Never" report_fixme = "Never"
reorder_imports = false reorder_imports = false
expr_indent_style = "Tabbed"

View File

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

View File

@ -14,6 +14,10 @@ fn foo() -> bool {
(((((((((aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + a + (((((((((aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + a +
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaa))))))))); aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaa)))))))));
{ for _ in 0..10 {} }
{{{{}}}}
if 1 + 2 > 0 { let result = 5; result } else { 4}; if 1 + 2 > 0 { let result = 5; result } else { 4};
if let Some(x) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa { if let Some(x) = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa {
@ -30,6 +34,10 @@ fn foo() -> bool {
if let (some_very_large, if let (some_very_large,
tuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuple) = 1111 + 2222 {} tuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuple) = 1111 + 2222 {}
if let (some_very_large, tuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuple) = 1
+ 2 + 3 {
}
if cond() { if cond() {
something(); something();
} else if different_cond() { } else if different_cond() {
@ -39,3 +47,19 @@ fn foo() -> bool {
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
} }
} }
fn bar() {
let range = ( 111111111 + 333333333333333333 + 1111 + 400000000000000000) .. (2222 + 2333333333333333);
let another_range = 5..some_func( a , b /* comment */);
for _ in 1 ..{ call_forever(); }
syntactically_correct(loop { sup( '?'); }, if cond { 0 } else { 1 });
let third = ..10;
let infi_range = ..;
let foo = 1..;
let bar = 5;
let nonsense = (10 .. 0)..(0..10);
}

View File

@ -22,6 +22,8 @@ fn main() {
Foo { a:Bar, Foo { a:Bar,
b:foo() }; b:foo() };
Quux { x: if cond { bar(); }, y: baz() };
A { A {
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor.
first: item(), first: item(),
@ -37,3 +39,12 @@ fn main() {
* o o o o */ * o o o o */
graph: G, } graph: G, }
} }
fn matcher() {
TagTerminatedByteMatcher {
matcher: ByteMatcher {
pattern: b"<HTML",
mask: b"\xFF\xDF\xDF\xDF\xDF\xFF",
},
};
}

View File

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

View File

@ -15,6 +15,20 @@ fn foo() -> bool {
a + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + a + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +
aaaaa))))))))); aaaaa)))))))));
{
for _ in 0..10 {
}
}
{
{
{
{
}
}
}
}
if 1 + 2 > 0 { if 1 + 2 > 0 {
let result = 5; let result = 5;
result result
@ -39,6 +53,10 @@ fn foo() -> bool {
2222 { 2222 {
} }
if let (some_very_large, tuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuple) =
1 + 2 + 3 {
}
if cond() { if cond() {
something(); something();
} else if different_cond() { } else if different_cond() {
@ -49,3 +67,29 @@ fn foo() -> bool {
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
} }
} }
fn bar() {
let range = (111111111 + 333333333333333333 + 1111 + 400000000000000000)..(2222 +
2333333333333333);
let another_range = 5..some_func(a, b /* comment */);
for _ in 1.. {
call_forever();
}
syntactically_correct(loop {
sup('?');
},
if cond {
0
} else {
1
});
let third = ..10;
let infi_range = ..;
let foo = 1..;
let bar = 5;
let nonsense = (10..0)..(0..10);
}

View File

@ -29,9 +29,16 @@ fn main() {
Foo { a: Bar, b: foo() }; Foo { a: Bar, b: foo() };
Quux {
x: if cond {
bar();
},
y: baz(),
};
A { A {
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit
// sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante // amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante
// hendrerit. Donec et mollis dolor. // hendrerit. Donec et mollis dolor.
first: item(), first: item(),
// Praesent et diam eget libero egestas mattis sit amet vitae augue. // Praesent et diam eget libero egestas mattis sit amet vitae augue.
@ -48,3 +55,12 @@ fn main() {
graph: G, graph: G,
} }
} }
fn matcher() {
TagTerminatedByteMatcher {
matcher: ByteMatcher {
pattern: b"<HTML",
mask: b"\xFF\xDF\xDF\xDF\xDF\xFF",
},
};
}