diff --git a/src/config.rs b/src/config.rs index c7525e930ec..95994531950 100644 --- a/src/config.rs +++ b/src/config.rs @@ -45,6 +45,26 @@ impl Density { } } +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum MultilineStyle { + // Use horizontal layout if it fits in one line, fall back to vertical + PreferSingle, + // Use vertical layout + ForceMulti, +} + + +impl_enum_decodable!(MultilineStyle, PreferSingle, ForceMulti); + +impl MultilineStyle { + pub fn to_list_tactic(self) -> ListTactic { + match self { + MultilineStyle::PreferSingle => ListTactic::HorizontalVertical, + MultilineStyle::ForceMulti => ListTactic::Vertical, + } + } +} + macro_rules! create_config { ($($i:ident: $ty:ty),+ $(,)*) => ( #[derive(RustcDecodable, Clone)] @@ -122,6 +142,7 @@ create_config! { struct_trailing_comma: SeparatorTactic, struct_lit_trailing_comma: SeparatorTactic, struct_lit_style: StructLitStyle, + struct_lit_multiline_style: MultilineStyle, enum_trailing_comma: bool, report_todo: ReportTactic, report_fixme: ReportTactic, @@ -155,6 +176,7 @@ impl Default for Config { struct_trailing_comma: SeparatorTactic::Vertical, struct_lit_trailing_comma: SeparatorTactic::Vertical, struct_lit_style: StructLitStyle::Block, + struct_lit_multiline_style: MultilineStyle::PreferSingle, enum_trailing_comma: true, report_todo: ReportTactic::Always, report_fixme: ReportTactic::Never, diff --git a/src/expr.rs b/src/expr.rs index e3146170f82..2923aa45097 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -17,7 +17,7 @@ use StructLitStyle; use utils::{span_after, make_indent, extra_offset, first_line_width, last_line_width, wrap_str, binary_search}; use visitor::FmtVisitor; -use config::BlockIndentStyle; +use config::{BlockIndentStyle, MultilineStyle}; use comment::{FindUncommented, rewrite_comment, contains_comment}; use types::rewrite_path; use items::{span_lo_for_arg, span_hi_for_arg, rewrite_fn_input}; @@ -1019,7 +1019,10 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext, span.hi); let fmt = ListFormatting { - tactic: ListTactic::HorizontalVertical, + tactic: match (context.config.struct_lit_style, fields.len()) { + (StructLitStyle::Visual, 1) => ListTactic::HorizontalVertical, + _ => context.config.struct_lit_multiline_style.to_list_tactic(), + }, separator: ",", trailing_separator: if base.is_some() { SeparatorTactic::Never @@ -1033,12 +1036,16 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext, }; let fields_str = try_opt!(write_list(&items.collect::>(), &fmt)); - match context.config.struct_lit_style { - StructLitStyle::Block 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)) - } + let format_on_newline = || { + let inner_indent = make_indent(context.block_indent + + context.config.tab_spaces); + let outer_indent = make_indent(context.block_indent); + Some(format!("{} {{\n{}{}\n{}}}", path_str, inner_indent, fields_str, outer_indent)) + }; + + match (context.config.struct_lit_style, context.config.struct_lit_multiline_style) { + (StructLitStyle::Block, _) if fields_str.contains('\n') => format_on_newline(), + (StructLitStyle::Block, MultilineStyle::ForceMulti) => format_on_newline(), _ => Some(format!("{} {{ {} }}", path_str, fields_str)), } diff --git a/tests/source/struct_lits_multiline.rs b/tests/source/struct_lits_multiline.rs new file mode 100644 index 00000000000..d0cf7d4069f --- /dev/null +++ b/tests/source/struct_lits_multiline.rs @@ -0,0 +1,72 @@ +// rustfmt-struct_lit_multiline_style: ForceMulti + +// Struct literal expressions. + +fn main() { + let x = Bar; + + // Comment + let y = Foo {a: x }; + + Foo { a: foo() /* comment*/, /* comment*/ b: bar(), ..something }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(), b: bar(), }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(), b: bar(), }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { + // Comment + a: foo(), // Comment + // Comment + b: bar(), // Comment + }; + + Foo { a:Bar, + b:foo() }; + + Quux { x: if cond { bar(); }, y: baz() }; + + 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 + }; + + Some(Data::MethodCallData(MethodCallData { + span: sub_span.unwrap(), + scope: self.enclosing_scope(id), + ref_id: def_id, + decl_id: Some(decl_id), + })); + + Diagram { /* o This graph demonstrates how + * / \ significant whitespace is + * o o preserved. + * /|\ \ + * o o o o */ + graph: G, } +} + +fn matcher() { + TagTerminatedByteMatcher { + matcher: ByteMatcher { + pattern: b" { memb: T } + let foo = Foo:: { memb: 10 }; +} + +fn issue201() { + let s = S{a:0, .. b}; +} + +fn issue201_2() { + let s = S{a: S2{ .. c}, .. b}; +} diff --git a/tests/source/struct_lits_visual_multiline.rs b/tests/source/struct_lits_visual_multiline.rs new file mode 100644 index 00000000000..192d88a3903 --- /dev/null +++ b/tests/source/struct_lits_visual_multiline.rs @@ -0,0 +1,42 @@ +// rustfmt-struct_lit_style: Visual +// rustfmt-struct_lit_multiline_style: ForceMulti + +// 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() }; + + Quux { x: if cond { bar(); }, y: baz() }; + + 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, } +} diff --git a/tests/target/struct_lits_multiline.rs b/tests/target/struct_lits_multiline.rs new file mode 100644 index 00000000000..2670ef6de1d --- /dev/null +++ b/tests/target/struct_lits_multiline.rs @@ -0,0 +1,108 @@ +// rustfmt-struct_lit_multiline_style: ForceMulti + +// Struct literal expressions. + +fn main() { + let x = Bar; + + // Comment + let y = Foo { + a: x, + }; + + Foo { + a: foo(), // comment + // comment + b: bar(), + ..something + }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { + a: foo(), + b: bar(), + }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { + a: foo(), + b: bar(), + }; + + Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { + // Comment + a: foo(), // Comment + // Comment + b: bar(), /* Comment */ + }; + + Foo { + a: Bar, + b: foo(), + }; + + Quux { + x: if cond { + bar(); + }, + y: baz(), + }; + + 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, + }; + + Some(Data::MethodCallData(MethodCallData { + span: sub_span.unwrap(), + scope: self.enclosing_scope(id), + ref_id: def_id, + decl_id: Some(decl_id), + })); + + Diagram { + // o This graph demonstrates how + // / \ significant whitespace is + // o o preserved. + // /|\ \ + // o o o o + graph: G, + } +} + +fn matcher() { + TagTerminatedByteMatcher { + matcher: ByteMatcher { + pattern: b" { + memb: T, + } + let foo = Foo:: { + memb: 10, + }; +} + +fn issue201() { + let s = S { + a: 0, + ..b + }; +} + +fn issue201_2() { + let s = S { + a: S2 { + ..c + }, + ..b + }; +} diff --git a/tests/target/struct_lits_visual_multiline.rs b/tests/target/struct_lits_visual_multiline.rs new file mode 100644 index 00000000000..94aa121b7a4 --- /dev/null +++ b/tests/target/struct_lits_visual_multiline.rs @@ -0,0 +1,61 @@ +// rustfmt-struct_lit_style: Visual +// rustfmt-struct_lit_multiline_style: ForceMulti + +// 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 { // Commen + // t + a: foo(), /* C + * o + * m + * m + * e + * n + * t */ + // Commen + // t + b: bar(), /* C + * o + * m + * m + * e + * n + * t */ }; + + Foo { a: Bar, + b: foo(), }; + + Quux { x: if cond { + bar(); + }, + y: baz(), }; + + 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, } +}