Support pre- and post-comments for enums

Use lists to format enum variants rather than special formatting.
Add tests for enums mostly around block comments.
This commit is contained in:
mwiczer 2015-10-07 19:23:07 -04:00
parent 8880c0e5d3
commit 5162282b60
6 changed files with 310 additions and 42 deletions

View File

@ -592,31 +592,88 @@ impl<'a> FmtVisitor<'a> {
self.buffer.push_str(&generics_str);
self.last_pos = body_start;
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
} else {
enum_def.variants[i + 1].span.lo
};
self.visit_variant(f, i == enum_def.variants.len() - 1, next_span_start);
self.block_indent = self.block_indent.block_indent(self.config);
let variant_list = self.format_variant_list(enum_def, body_start, span.hi - BytePos(1));
match variant_list {
Some(ref body_str) => self.buffer.push_str(&body_str),
None => self.format_missing(span.hi - BytePos(1)),
}
self.block_indent = self.block_indent.block_unindent(self.config);
self.format_missing_with_indent(span.hi - BytePos(1));
if variant_list.is_some() {
self.buffer.push_str(&self.block_indent.to_string(self.config));
}
self.buffer.push_str("}");
self.last_pos = span.hi;
}
// Format the body of an enum definition
fn format_variant_list(&self,
enum_def: &ast::EnumDef,
body_lo: BytePos,
body_hi: BytePos)
-> Option<String> {
if enum_def.variants.is_empty() {
return None;
}
let mut result = String::with_capacity(1024);
result.push('\n');
let indentation = self.block_indent.to_string(self.config);
result.push_str(&indentation);
let items = itemize_list(self.codemap,
enum_def.variants.iter(),
"}",
|f| {
if !f.node.attrs.is_empty() {
f.node.attrs[0].span.lo
} else {
f.span.lo
}
},
|f| f.span.hi,
|f| self.format_variant(f),
body_lo,
body_hi);
let budget = self.config.max_width - self.block_indent.width() - 2;
let fmt = ListFormatting {
tactic: DefinitiveListTactic::Vertical,
separator: ",",
trailing_separator: SeparatorTactic::Always,
indent: self.block_indent,
width: budget,
ends_with_newline: true,
config: self.config,
};
let list = try_opt!(write_list(items, &fmt));
result.push_str(&list);
result.push('\n');
Some(result)
}
// Variant of an enum.
fn visit_variant(&mut self, field: &ast::Variant, last_field: bool, next_span_start: BytePos) {
if self.visit_attrs(&field.node.attrs) {
return;
fn format_variant(&self, field: &ast::Variant) -> Option<String> {
if contains_skip(&field.node.attrs) {
let lo = field.node.attrs[0].span.lo;
let span = codemap::mk_sp(lo, field.span.hi);
return Some(self.snippet(span));
}
self.format_missing_with_indent(field.span.lo);
let indent = self.block_indent;
let mut result = try_opt!(field.node
.attrs
.rewrite(&self.get_context(),
self.config.max_width - indent.width(),
indent));
if !result.is_empty() {
result.push('\n');
result.push_str(&indent.to_string(self.config));
}
let result = match field.node.kind {
let variant_body = match field.node.kind {
ast::VariantKind::TupleVariantKind(ref types) => {
let mut result = field.node.name.to_string();
@ -633,12 +690,12 @@ impl<'a> FmtVisitor<'a> {
Indent::empty())
},
span_after(field.span, "(", self.codemap),
next_span_start);
field.span.hi);
let item_vec = items.collect::<Vec<_>>();
result.push('(');
let indent = self.block_indent + field.node.name.to_string().len() + "(".len();
let indent = indent + field.node.name.to_string().len() + "(".len();
let comma_cost = if self.config.enum_trailing_comma {
1
@ -659,10 +716,7 @@ impl<'a> FmtVisitor<'a> {
ends_with_newline: true,
config: self.config,
};
let list_str = match write_list(&item_vec, &fmt) {
Some(list_str) => list_str,
None => return,
};
let list_str = try_opt!(write_list(&item_vec, &fmt));
result.push_str(&list_str);
result.push(')');
@ -674,31 +728,26 @@ impl<'a> FmtVisitor<'a> {
result.push_str(&expr_snippet);
}
result
Some(result)
}
ast::VariantKind::StructVariantKind(ref struct_def) => {
// TODO: Should limit the width, as we have a trailing comma
let struct_rewrite = self.format_struct("",
field.node.name,
ast::Visibility::Inherited,
struct_def,
None,
field.span,
self.block_indent);
match struct_rewrite {
Some(struct_str) => struct_str,
None => return,
}
self.format_struct("",
field.node.name,
ast::Visibility::Inherited,
struct_def,
None,
field.span,
indent)
}
};
self.buffer.push_str(&result);
if !last_field || self.config.enum_trailing_comma {
self.buffer.push_str(",");
if let Some(variant_str) = variant_body {
result.push_str(&variant_str);
Some(result)
} else {
None
}
self.last_pos = field.span.hi + BytePos(1);
}
fn format_struct(&self,

View File

@ -208,7 +208,18 @@ pub fn write_list<'b, I, T>(items: I, formatting: &ListFormatting<'b>) -> Option
} else {
0
};
let item_width = inner_item.len() + item_sep_len;
// Item string may be multi-line. Its length (used for block comment alignment)
// Should be only the length of the last line.
let item_last_line = if item.is_multiline() {
inner_item.lines().last().unwrap_or("")
} else {
inner_item.as_ref()
};
let mut item_last_line_width = item_last_line.len() + item_sep_len;
if item_last_line.starts_with(indent_str) {
item_last_line_width -= indent_str.len();
}
match tactic {
DefinitiveListTactic::Horizontal if !first => {
@ -284,10 +295,12 @@ pub fn write_list<'b, I, T>(items: I, formatting: &ListFormatting<'b>) -> Option
if tactic == DefinitiveListTactic::Vertical && item.post_comment.is_some() {
// 1 = space between item and comment.
let width = formatting.width.checked_sub(item_width + 1).unwrap_or(1);
let width = formatting.width.checked_sub(item_last_line_width + 1).unwrap_or(1);
let mut offset = formatting.indent;
offset.alignment += item_width + 1;
offset.alignment += item_last_line_width + 1;
let comment = item.post_comment.as_ref().unwrap();
debug!("Width = {}, offset = {:?}", width, offset);
// Use block-style only for the last item or multiline comments.
let block_style = !formatting.ends_with_newline && last ||
comment.trim().contains('\n') ||

94
tests/source/enum.rs Normal file
View File

@ -0,0 +1,94 @@
// Enums test
#[atrr]
pub enum Test {
A, B(u32,
A /* comment */,
SomeType),
/// Doc comment
C,
}
pub enum Foo<'a, Y: Baz> where X: Whatever
{ A, }
enum EmtpyWithComment {
// Some comment
}
// C-style enum
enum Bar {
A = 1,
#[someAttr(test)]
B = 2, // comment
C,
}
enum LongVariants {
First(LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG, // comment
VARIANT),
// This is the second variant
Second
}
enum StructLikeVariants {
Normal(u32, String, ),
StructLike { x: i32, // Test comment
// Pre-comment
#[Attr50] y: SomeType, // Aanother Comment
}, SL { a: A }
}
enum X {
CreateWebGLPaintTask(Size2D<i32>, GLContextAttributes, IpcSender<Result<(IpcSender<CanvasMsg>, usize), String>>), // This is a post comment
}
pub enum EnumWithAttributes {
//This is a pre comment AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
TupleVar(usize, usize, usize), // AAAA AAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
// Pre Comment
#[rustfmt_skip]
SkippedItem(String,String,), // Post-comment
#[another_attr]
#[attr2]
ItemStruct {x: usize, y: usize}, // Comment AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
// And another
ForcedPreflight // AAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
}
pub enum SingleTuple {
// Pre Comment AAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Match(usize, usize, String) // Post-comment AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
}
pub enum SingleStruct {
Match {name: String, loc: usize} // Post-comment
}
pub enum GenericEnum<I, T>
where I: Iterator<Item = T> {
// Pre Comment
Left {list: I, root: T}, // Post-comment
Right {list: I, root: T} // Post Comment
}
enum EmtpyWithComment {
// Some comment
}
enum TestFormatFails {
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
}
fn nested_enum_test() {
if true {
enum TestEnum {
One(usize, usize, usize, usize, usize, usize, usize, usize, usize, usize, usize, usize, usize, usize, usize, usize,), // AAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAA
Two // AAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAA
}
enum TestNestedFormatFail {
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
}
}
}

View File

@ -105,3 +105,13 @@ pub struct State<F: FnMut()> { now: F }
struct Palette { /// A map of indizes in the palette to a count of pixels in approximately that color
foo: i32}
// Splitting a single line comment into a block previously had a misalignment
// when the field had attributes
struct FieldsWithAttributes {
// Pre Comment
#[rustfmt_skip] pub host:String, // Post comment BBBBBBBBBBBBBB BBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBB BBBBBBBBBBB
//Another pre comment
#[attr1]
#[attr2] pub id: usize // CCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCC CCCCCCCCCCCC
}

View File

@ -49,5 +49,94 @@ enum StructLikeVariants {
enum X {
CreateWebGLPaintTask(Size2D<i32>,
GLContextAttributes,
IpcSender<Result<(IpcSender<CanvasMsg>, usize), String>>),
IpcSender<Result<(IpcSender<CanvasMsg>, usize), String>>), /* This is
* a post c
* omment */
}
pub enum EnumWithAttributes {
// This is a pre comment
// AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
TupleVar(usize, usize, usize), /* AAAA AAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAA
* AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA */
// Pre Comment
#[rustfmt_skip]
SkippedItem(String,String,), // Post-comment
#[another_attr]
#[attr2]
ItemStruct {
x: usize,
y: usize,
}, /* Comment AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
* AAAAAAAAAAAAAAAAAAA */
// And another
ForcedPreflight, /* AAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
* AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA */
}
pub enum SingleTuple {
// Pre Comment AAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
// AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Match(usize, usize, String), /* Post-comment
* AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
* A */
}
pub enum SingleStruct {
Match {
name: String,
loc: usize,
}, // Post-comment
}
pub enum GenericEnum<I, T>
where I: Iterator<Item = T>
{
// Pre Comment
Left {
list: I,
root: T,
}, // Post-comment
Right {
list: I,
root: T,
}, // Post Comment
}
enum EmtpyWithComment {
// Some comment
}
enum TestFormatFails {
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
}
fn nested_enum_test() {
if true {
enum TestEnum {
One(usize,
usize,
usize,
usize,
usize,
usize,
usize,
usize,
usize,
usize,
usize,
usize,
usize,
usize,
usize,
usize), /* AAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAA
* AAAAAAAAAAAAAAAAAAAAAA */
Two, /* AAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
* AAAAAAAAAAAAAAAAAA */
}
enum TestNestedFormatFail {
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
}
}
}

View File

@ -100,3 +100,16 @@ struct Palette {
/// A map of indizes in the palette to a count of pixels in approximately that color
foo: i32,
}
// Splitting a single line comment into a block previously had a misalignment
// when the field had attributes
struct FieldsWithAttributes {
// Pre Comment
#[rustfmt_skip] pub host:String, /* Post comment BBBBBBBBBBBBBB BBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBB
* BBBBBBBBBBBBBBBBB BBBBBBBBBBB */
// Another pre comment
#[attr1]
#[attr2]
pub id: usize, /* CCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCC
* CCCCCCCCCCCCCC CCCCCCCCCCCC */
}