Merge pull request #137 from nrc/struct-lit-opt

Options for formatting struct literals
This commit is contained in:
Nick Cameron 2015-07-17 06:57:01 +12:00
commit c3375078e5
17 changed files with 306 additions and 128 deletions

View File

@ -35,9 +35,11 @@ pub struct ChangeSet<'a> {
impl<'a> ChangeSet<'a> {
// Create a new ChangeSet for a given libsyntax CodeMap.
pub fn from_codemap(codemap: &'a CodeMap) -> ChangeSet<'a> {
let mut result = ChangeSet { file_map: HashMap::new(),
codemap: codemap,
file_spans: Vec::with_capacity(codemap.files.borrow().len()), };
let mut result = ChangeSet {
file_map: HashMap::new(),
codemap: codemap,
file_spans: Vec::with_capacity(codemap.files.borrow().len()),
};
for f in codemap.files.borrow().iter() {
// Use the length of the file as a heuristic for how much space we

View File

@ -24,13 +24,15 @@ 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 fmt = StringFormat { opener: "",
closer: "",
line_start: line_start,
line_end: "",
width: max_chars,
offset: offset + opener.len() - line_start.len(),
trim_end: true, };
let fmt = StringFormat {
opener: "",
closer: "",
line_start: line_start,
line_end: "",
width: max_chars,
offset: offset + opener.len() - line_start.len(),
trim_end: true,
};
let indent_str = make_indent(offset);
let line_breaks = s.chars().filter(|&c| c == '\n').count();

View File

@ -10,7 +10,7 @@
extern crate toml;
use {NewlineStyle, BraceStyle, ReturnIndent};
use {NewlineStyle, BraceStyle, ReturnIndent, StructLitStyle};
use lists::SeparatorTactic;
use issues::ReportTactic;
@ -26,6 +26,7 @@ pub struct Config {
pub fn_args_paren_newline: bool,
pub struct_trailing_comma: SeparatorTactic,
pub struct_lit_trailing_comma: SeparatorTactic,
pub struct_lit_style: StructLitStyle,
pub enum_trailing_comma: bool,
pub report_todo: ReportTactic,
pub report_fixme: ReportTactic,
@ -35,6 +36,14 @@ pub struct Config {
impl Config {
pub fn from_toml(toml: &str) -> Config {
let parsed = toml.parse().unwrap();
toml::decode(parsed).unwrap()
match toml::decode(parsed) {
Some(decoded) => decoded,
None => {
println!("Decoding config file failed. Config:\n{}", toml);
let parsed: toml::Value = toml.parse().unwrap();
println!("\n\nParsed:\n{:?}", parsed);
panic!();
}
}
}
}

View File

@ -7,6 +7,7 @@ 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"

View File

@ -11,6 +11,7 @@
use rewrite::{Rewrite, RewriteContext};
use lists::{write_list, itemize_list, ListFormatting, SeparatorTactic, ListTactic};
use string::{StringFormat, rewrite_string};
use StructLitStyle;
use utils::{span_after, make_indent};
use visitor::FmtVisitor;
@ -100,13 +101,15 @@ fn rewrite_string_lit(context: &RewriteContext,
if l_loc.line == r_loc.line && r_loc.col.to_usize() <= context.config.max_width {
return context.codemap.span_to_snippet(span).ok();
}
let fmt = StringFormat { opener: "\"",
closer: "\"",
line_start: " ",
line_end: "\\",
width: width,
offset: offset,
trim_end: false, };
let fmt = StringFormat {
opener: "\"",
closer: "\"",
line_start: " ",
line_end: "\\",
width: width,
offset: offset,
trim_end: false,
};
Some(rewrite_string(&s.escape_default(), &fmt))
}
@ -146,13 +149,15 @@ fn rewrite_call(context: &RewriteContext,
callee.span.hi + BytePos(1),
span.hi);
let fmt = ListFormatting { tactic: ListTactic::HorizontalVertical,
separator: ",",
trailing_separator: SeparatorTactic::Never,
indent: offset,
h_width: remaining_width,
v_width: remaining_width,
ends_with_newline: true, };
let fmt = ListFormatting {
tactic: ListTactic::HorizontalVertical,
separator: ",",
trailing_separator: SeparatorTactic::Never,
indent: offset,
h_width: remaining_width,
v_width: remaining_width,
ends_with_newline: true,
};
Some(format!("{}({})", callee_str, write_list(&items, &fmt)))
}
@ -187,9 +192,19 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
}
let path_str = pprust::path_to_string(path);
// Foo { a: Foo } - indent is +3, width is -5.
let indent = offset + path_str.len() + 3;
let budget = width - (path_str.len() + 5);
let (indent, h_budget, v_budget) = match context.config.struct_lit_style {
StructLitStyle::VisualIndent => {
// Foo { a: Foo } - indent is +3, width is -5.
let budget = width - (path_str.len() + 5);
(offset + path_str.len() + 3, budget, budget)
}
StructLitStyle::BlockIndent => {
// 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;
(indent, width - (path_str.len() + 5), width - indent)
}
};
let field_iter = fields.into_iter().map(StructLitField::Regular)
.chain(base.into_iter().map(StructLitField::Base));
@ -215,13 +230,13 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
|item| {
match *item {
StructLitField::Regular(ref field) => {
rewrite_field(context, &field, budget, indent)
rewrite_field(context, &field, h_budget, indent)
.unwrap_or(context.codemap.span_to_snippet(field.span)
.unwrap())
},
StructLitField::Base(ref expr) => {
// 2 = ..
expr.rewrite(context, budget - 2, indent + 2)
expr.rewrite(context, h_budget - 2, indent + 2)
.map(|s| format!("..{}", s))
.unwrap_or(context.codemap.span_to_snippet(expr.span)
.unwrap())
@ -231,24 +246,32 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
span_after(span, "{", context.codemap),
span.hi);
let fmt = ListFormatting { tactic: ListTactic::HorizontalVertical,
separator: ",",
trailing_separator: if base.is_some() {
let fmt = ListFormatting {
tactic: ListTactic::HorizontalVertical,
separator: ",",
trailing_separator: if base.is_some() {
SeparatorTactic::Never
} else {
context.config.struct_lit_trailing_comma
},
indent: indent,
h_width: budget,
v_width: budget,
ends_with_newline: true, };
indent: indent,
h_width: h_budget,
v_width: v_budget,
ends_with_newline: true,
};
let fields_str = write_list(&items, &fmt);
Some(format!("{} {{ {} }}", path_str, fields_str))
// FIXME if the usual multi-line layout is too wide, we should fall back to
// Foo {
// a: ...,
// }
match context.config.struct_lit_style {
StructLitStyle::BlockIndent if fields_str.contains('\n') => {
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))
}
_ => Some(format!("{} {{ {} }}", path_str, fields_str)),
}
// FIXME if context.config.struct_lit_style == VisualIndent, but we run out
// of space, we should fall back to BlockIndent.
}
fn rewrite_field(context: &RewriteContext,
@ -291,13 +314,15 @@ fn rewrite_tuple_lit(context: &RewriteContext,
span.lo + BytePos(1), // Remove parens
span.hi - BytePos(1));
let fmt = ListFormatting { tactic: ListTactic::HorizontalVertical,
separator: ",",
trailing_separator: SeparatorTactic::Never,
indent: indent,
h_width: width - 2,
v_width: width - 2,
ends_with_newline: true, };
let fmt = ListFormatting {
tactic: ListTactic::HorizontalVertical,
separator: ",",
trailing_separator: SeparatorTactic::Never,
indent: indent,
h_width: width - 2,
v_width: width - 2,
ends_with_newline: true,
};
Some(format!("({})", write_list(&items, &fmt)))
}

View File

@ -71,13 +71,15 @@ impl<'a> FmtVisitor<'a> {
let remaining_line_budget = one_line_budget.checked_sub(used_width).unwrap_or(0);
let remaining_multi_budget = multi_line_budget.checked_sub(used_width).unwrap_or(0);
let fmt = ListFormatting { tactic: ListTactic::Mixed,
separator: ",",
trailing_separator: SeparatorTactic::Never,
indent: block_indent + indent,
h_width: remaining_line_budget,
v_width: remaining_multi_budget,
ends_with_newline: true, };
let fmt = ListFormatting {
tactic: ListTactic::Mixed,
separator: ",",
trailing_separator: SeparatorTactic::Never,
indent: block_indent + indent,
h_width: remaining_line_budget,
v_width: remaining_multi_budget,
ends_with_newline: true,
};
let mut items = itemize_list(self.codemap,
vec![ListItem::from_str("")], /* Dummy value, explanation

View File

@ -96,9 +96,11 @@ pub struct BadIssueSeeker {
impl BadIssueSeeker {
pub fn new(report_todo: ReportTactic, report_fixme: ReportTactic) -> BadIssueSeeker {
BadIssueSeeker { state: Seeking::Issue { todo_idx: 0, fixme_idx: 0 },
report_todo: report_todo,
report_fixme: report_fixme, }
BadIssueSeeker {
state: Seeking::Issue { todo_idx: 0, fixme_idx: 0 },
report_todo: report_todo,
report_fixme: report_fixme,
}
}
// Check whether or not the current char is conclusive evidence for an

View File

@ -305,13 +305,15 @@ impl<'a> FmtVisitor<'a> {
item.item = arg;
}
let fmt = ListFormatting { tactic: ListTactic::HorizontalVertical,
separator: ",",
trailing_separator: SeparatorTactic::Never,
indent: arg_indent,
h_width: one_line_budget,
v_width: multi_line_budget,
ends_with_newline: true, };
let fmt = ListFormatting {
tactic: ListTactic::HorizontalVertical,
separator: ",",
trailing_separator: SeparatorTactic::Never,
indent: arg_indent,
h_width: one_line_budget,
v_width: multi_line_budget,
ends_with_newline: true,
};
write_list(&arg_items, &fmt)
}
@ -567,13 +569,15 @@ impl<'a> FmtVisitor<'a> {
// 1 = ,
let budget = self.config.ideal_width - offset + self.config.tab_spaces - 1;
let fmt = ListFormatting { tactic: tactic,
separator: ",",
trailing_separator: self.config.struct_trailing_comma,
indent: offset + self.config.tab_spaces,
h_width: self.config.max_width,
v_width: budget,
ends_with_newline: false, };
let fmt = ListFormatting {
tactic: tactic,
separator: ",",
trailing_separator: self.config.struct_trailing_comma,
indent: offset + self.config.tab_spaces,
h_width: self.config.max_width,
v_width: budget,
ends_with_newline: false,
};
result.push_str(&write_list(&items, &fmt));
@ -708,13 +712,15 @@ impl<'a> FmtVisitor<'a> {
item.item = ty;
}
let fmt = ListFormatting { tactic: ListTactic::HorizontalVertical,
separator: ",",
trailing_separator: SeparatorTactic::Never,
indent: offset + 1,
h_width: budget,
v_width: budget,
ends_with_newline: true, };
let fmt = ListFormatting {
tactic: ListTactic::HorizontalVertical,
separator: ",",
trailing_separator: SeparatorTactic::Never,
indent: offset + 1,
h_width: budget,
v_width: budget,
ends_with_newline: true,
};
result.push_str(&write_list(&items, &fmt));
result.push('>');
@ -749,13 +755,15 @@ impl<'a> FmtVisitor<'a> {
span_end);
let budget = self.config.ideal_width + self.config.leeway - indent - 10;
let fmt = ListFormatting { tactic: ListTactic::Vertical,
separator: ",",
trailing_separator: SeparatorTactic::Never,
indent: indent + 10,
h_width: budget,
v_width: budget,
ends_with_newline: true, };
let fmt = ListFormatting {
tactic: ListTactic::Vertical,
separator: ",",
trailing_separator: SeparatorTactic::Never,
indent: indent + 10,
h_width: budget,
v_width: budget,
ends_with_newline: true,
};
result.push_str(&write_list(&items, &fmt));
result

View File

@ -111,6 +111,19 @@ pub enum ReturnIndent {
impl_enum_decodable!(ReturnIndent, WithArgs, WithWhereClause);
// How to stle a struct literal.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum StructLitStyle {
// First line on the same line as the opening brace, all lines aligned with
// the first line.
VisualIndent,
// First line is on a new line and all lines align with block indent.
BlockIndent,
// FIXME Maybe we should also have an option to align types.
}
impl_enum_decodable!(StructLitStyle, VisualIndent, BlockIndent);
enum ErrorKind {
// Line has exceeded character limit
LineOverflow,

View File

@ -38,9 +38,11 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> {
self.codemap.lookup_char_pos(ex.span.hi));
self.format_missing(ex.span.lo);
let offset = self.changes.cur_offset_span(ex.span);
let context = RewriteContext { codemap: self.codemap,
config: self.config,
block_indent: self.block_indent, };
let context = RewriteContext {
codemap: self.codemap,
config: self.config,
block_indent: self.block_indent,
};
let rewrite = ex.rewrite(&context, self.config.max_width - offset, offset);
if let Some(new_str) = rewrite {
@ -284,11 +286,13 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> {
impl<'a> FmtVisitor<'a> {
pub fn from_codemap<'b>(codemap: &'b CodeMap, config: &'b Config) -> FmtVisitor<'b> {
FmtVisitor { codemap: codemap,
changes: ChangeSet::from_codemap(codemap),
last_pos: BytePos(0),
block_indent: 0,
config: config, }
FmtVisitor {
codemap: codemap,
changes: ChangeSet::from_codemap(codemap),
last_pos: BytePos(0),
block_indent: 0,
config: config,
}
}
pub fn snippet(&self, span: Span) -> String {

View File

@ -8,6 +8,7 @@ fn_return_indent = "WithArgs"
fn_args_paren_newline = true
struct_trailing_comma = "Vertical"
struct_lit_trailing_comma = "Vertical"
struct_lit_style = "BlockIndent"
enum_trailing_comma = true
report_todo = "Always"
report_fixme = "Never"

View File

@ -8,6 +8,7 @@ fn_return_indent = "WithArgs"
fn_args_paren_newline = true
struct_trailing_comma = "Vertical"
struct_lit_trailing_comma = "Vertical"
struct_lit_style = "BlockIndent"
enum_trailing_comma = true
report_todo = "Always"
report_fixme = "Never"

View File

@ -0,0 +1,15 @@
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 = "VisualIndent"
struct_lit_trailing_comma = "Vertical"
enum_trailing_comma = true
report_todo = "Always"
report_fixme = "Never"
reorder_imports = false

View File

@ -8,7 +8,9 @@ fn main() {
Foo { a: foo() /* comment*/, /* comment*/ b: bar(), ..something };
Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(), b: bar(), };
Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(), b: bar(), };
Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(), b: bar(), };
Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo {
// Comment

View File

@ -0,0 +1,39 @@
// rustfmt-config: visual_struct_lits.toml
// Struct literal expressions.
fn main() {
let x = Bar;
// Comment
let y = Foo {a: x };
Foo { a: foo() /* comment*/, /* comment*/ b: bar(), ..something };
Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(), b: bar(), };
Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo {
// Comment
a: foo(), // Comment
// Comment
b: bar(), // Comment
};
Foo { a:Bar,
b:foo() };
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.
first: item(),
// Praesent et diam eget libero egestas mattis sit amet vitae augue.
// Nam tincidunt congue enim, ut porta lorem lacinia consectetur.
second: Item
};
Diagram { /* o This graph demonstrates how
* / \ significant whitespace is
* o o preserved.
* /|\ \
* o o o o */
graph: G, }
}

View File

@ -6,45 +6,45 @@ fn main() {
// Comment
let y = Foo { a: x };
Foo { a: foo(), // comment
// comment
b: bar(),
..something };
Foo {
a: foo(), // comment
// comment
b: bar(),
..something
};
Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(),
b: bar(), };
Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(), b: bar() };
Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { // Comment
a: foo(), /* C
* o
* m
* m
* e
* n
* t */
// Comment
b: bar(), /* C
* o
* m
* m
* e
* n
* t */ };
Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo {
a: foo(),
b: bar(),
};
Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo {
// Comment
a: foo(), // Comment
// Comment
b: bar(), /* Comment */
};
Foo { a: Bar, b: foo() };
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
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.
first: item(),
// Praesent et diam eget libero egestas mattis sit amet vitae augue.
// Nam tincidunt congue enim, ut porta lorem lacinia consectetur.
second: Item, };
second: Item,
};
Diagram { // o This graph demonstrates how
// / \ significant whitespace is
// o o preserved.
// /|\ \
// o o o o
graph: G, }
Diagram {
// o This graph demonstrates how
// / \ significant whitespace is
// o o preserved.
// /|\ \
// o o o o
graph: G,
}
}

View File

@ -0,0 +1,52 @@
// rustfmt-config: visual_struct_lits.toml
// Struct literal expressions.
fn main() {
let x = Bar;
// Comment
let y = Foo { a: x };
Foo { a: foo(), // comment
// comment
b: bar(),
..something };
Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(),
b: bar(), };
Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { // Comment
a: foo(), /* C
* o
* m
* m
* e
* n
* t */
// Comment
b: bar(), /* C
* o
* m
* m
* e
* n
* t */ };
Foo { a: Bar, b: foo() };
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.
first: item(),
// Praesent et diam eget libero egestas mattis sit amet vitae augue.
// Nam tincidunt congue enim, ut porta lorem lacinia consectetur.
second: Item, };
Diagram { // o This graph demonstrates how
// / \ significant whitespace is
// o o preserved.
// /|\ \
// o o o o
graph: G, }
}