Format comments in struct literals

This commit is contained in:
Marcus Klaas 2015-06-24 01:11:29 +02:00
parent 0ef5db9496
commit 9f3ab0b5fe
5 changed files with 107 additions and 46 deletions

View File

@ -9,8 +9,9 @@
// except according to those terms.
use rewrite::{Rewrite, RewriteContext};
use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic, ListTactic};
use lists::{write_list, itemize_list, ListFormatting, SeparatorTactic, ListTactic};
use string::{StringFormat, rewrite_string};
use utils::span_after;
use syntax::{ast, ptr};
use syntax::codemap::{Pos, Span, BytePos};
@ -37,11 +38,13 @@ impl Rewrite for ast::Expr {
return rewrite_paren(context, subexpr, width, offset);
}
ast::Expr_::ExprStruct(ref path, ref fields, ref base) => {
return rewrite_struct_lit(context, path,
fields,
base.as_ref().map(|e| &**e),
width,
offset);
return rewrite_struct_lit(context,
path,
fields,
base.as_ref().map(|e| &**e),
self.span,
width,
offset);
}
ast::Expr_::ExprTup(ref items) => {
return rewrite_tuple_lit(context, items, self.span, width, offset);
@ -107,8 +110,10 @@ fn rewrite_call(context: &RewriteContext,
")",
|item| item.span.lo,
|item| item.span.hi,
// Take old span when rewrite fails.
|item| item.rewrite(context, remaining_width, offset)
.unwrap(), // FIXME: don't unwrap, take span literal
.unwrap_or(context.codemap.span_to_snippet(item.span)
.unwrap()),
callee.span.hi + BytePos(1),
span.hi);
@ -134,35 +139,68 @@ fn rewrite_paren(context: &RewriteContext, subexpr: &ast::Expr, width: usize, of
subexpr_str.map(|s| format!("({})", s))
}
fn rewrite_struct_lit(context: &RewriteContext,
path: &ast::Path,
fields: &[ast::Field],
base: Option<&ast::Expr>,
width: usize,
offset: usize)
fn rewrite_struct_lit<'a>(context: &RewriteContext,
path: &ast::Path,
fields: &'a [ast::Field],
base: Option<&'a ast::Expr>,
span: Span,
width: usize,
offset: usize)
-> Option<String>
{
debug!("rewrite_struct_lit: width {}, offset {}", width, offset);
assert!(fields.len() > 0 || base.is_some());
enum StructLitField<'a> {
Regular(&'a ast::Field),
Base(&'a ast::Expr)
}
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 field_strs: Vec<_> =
try_opt!(fields.iter()
.map(|field| rewrite_field(context, field, budget, indent))
.chain(base.iter()
.map(|expr| expr.rewrite(context,
// 2 = ".."
budget - 2,
indent + 2)
.map(|s| format!("..{}", s))))
.collect());
let field_iter = fields.into_iter().map(StructLitField::Regular)
.chain(base.into_iter().map(StructLitField::Base));
let items = itemize_list(context.codemap,
Vec::new(),
field_iter,
",",
"}",
|item| {
match *item {
StructLitField::Regular(ref field) => field.span.lo,
// 2 = ..
StructLitField::Base(ref expr) => expr.span.lo - BytePos(2)
}
},
|item| {
match *item {
StructLitField::Regular(ref field) => field.span.hi,
StructLitField::Base(ref expr) => expr.span.hi
}
},
|item| {
match *item {
StructLitField::Regular(ref field) => {
rewrite_field(context, &field, budget, indent)
.unwrap_or(context.codemap.span_to_snippet(field.span)
.unwrap())
},
StructLitField::Base(ref expr) => {
// 2 = ..
expr.rewrite(context, budget - 2, indent + 2)
.map(|s| format!("..{}", s))
.unwrap_or(context.codemap.span_to_snippet(expr.span)
.unwrap())
}
}
},
span_after(span, "{", context.codemap),
span.hi);
// FIXME comments
let field_strs: Vec<_> = field_strs.into_iter().map(ListItem::from_str).collect();
let fmt = ListFormatting {
tactic: ListTactic::HorizontalVertical,
separator: ",",
@ -176,7 +214,7 @@ fn rewrite_struct_lit(context: &RewriteContext,
v_width: budget,
is_expression: true,
};
let fields_str = write_list(&field_strs, &fmt);
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
@ -210,7 +248,8 @@ fn rewrite_tuple_lit(context: &RewriteContext,
|item| item.rewrite(context,
context.config.max_width - indent - 2,
indent)
.unwrap(), // FIXME: don't unwrap, take span literal
.unwrap_or(context.codemap.span_to_snippet(item.span)
.unwrap()),
span.lo + BytePos(1), // Remove parens
span.hi - BytePos(1));

View File

@ -11,7 +11,7 @@
// Formatting top-level items - functions, structs, enums, traits, impls.
use {ReturnIndent, BraceStyle};
use utils::{format_visibility, make_indent, contains_skip};
use utils::{format_visibility, make_indent, contains_skip, span_after};
use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic, ListTactic};
use comment::FindUncommented;
use visitor::FmtVisitor;
@ -165,7 +165,7 @@ impl<'a> FmtVisitor<'a> {
one_line_budget,
multi_line_budget,
arg_indent,
codemap::mk_sp(self.span_after(span, "("),
codemap::mk_sp(span_after(span, "(", self.codemap),
span_for_return(&fd.output).lo)));
result.push(')');
@ -278,7 +278,7 @@ impl<'a> FmtVisitor<'a> {
// You also don't get to put a comment on self, unless it is explicit.
if args.len() >= min_args {
let comment_span_start = if min_args == 2 {
self.span_after(span, ",")
span_after(span, ",", self.codemap)
} else {
span.lo
};
@ -438,7 +438,7 @@ impl<'a> FmtVisitor<'a> {
|arg| arg.ty.span.lo,
|arg| arg.ty.span.hi,
|arg| pprust::ty_to_string(&arg.ty),
self.span_after(field.span, "("),
span_after(field.span, "(", self.codemap),
next_span_start);
result.push('(');
@ -549,7 +549,7 @@ impl<'a> FmtVisitor<'a> {
},
|field| field.node.ty.span.hi,
|field| self.format_field(field),
self.span_after(span, opener.trim()),
span_after(span, opener.trim(), self.codemap),
span.hi);
// 2 terminators and a semicolon
@ -714,7 +714,7 @@ impl<'a> FmtVisitor<'a> {
|sp| sp.lo,
|sp| sp.hi,
|_| String::new(),
self.span_after(span, "<"),
span_after(span, "<", self.codemap),
span.hi);
for (item, ty) in items.iter_mut().zip(lt_strs.chain(ty_strs)) {
@ -793,12 +793,6 @@ impl<'a> FmtVisitor<'a> {
pprust::pat_to_string(&arg.pat),
pprust::ty_to_string(&arg.ty))
}
fn span_after(&self, original: Span, needle: &str) -> BytePos {
let snippet = self.snippet(original);
original.lo + BytePos(snippet.find_uncommented(needle).unwrap() as u32 + 1)
}
}
fn span_for_return(ret: &ast::FunctionRetTy) -> Span {

View File

@ -203,10 +203,11 @@ pub fn write_list<'b>(items: &[ListItem], formatting: &ListFormatting<'b>) -> St
}
if tactic == ListTactic::Vertical && item.post_comment.is_some() {
let width = formatting.v_width - item_width - 1; // Space between item and comment
// 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 comment = item.post_comment.as_ref().unwrap();
// Use block-style only for the last item or multiline comments
// Use block-style only for the last item or multiline comments.
let block_style = formatting.is_expression && last ||
comment.trim().contains('\n') ||
comment.trim().len() > width;
@ -241,7 +242,7 @@ pub fn itemize_list<T, I, F1, F2, F3>(codemap: &CodeMap,
terminator: &str,
get_lo: F1,
get_hi: F2,
get_item: F3,
get_item_string: F3,
mut prev_span_end: BytePos,
next_span_start: BytePos)
-> Vec<ListItem>
@ -306,7 +307,7 @@ pub fn itemize_list<T, I, F1, F2, F3>(codemap: &CodeMap,
result.push(ListItem {
pre_comment: pre_comment,
item: get_item(&item),
item: get_item_string(&item),
post_comment: if post_snippet.len() > 0 {
Some(post_snippet.to_owned())
} else {

View File

@ -9,9 +9,19 @@
// except according to those terms.
use syntax::ast::{Visibility, Attribute, MetaItem, MetaItem_};
use syntax::codemap::{CodeMap, Span, BytePos};
use comment::FindUncommented;
use SKIP_ANNOTATION;
#[inline]
pub fn span_after(original: Span, needle: &str, codemap: &CodeMap) -> BytePos {
let snippet = codemap.span_to_snippet(original).unwrap();
original.lo + BytePos(snippet.find_uncommented(needle).unwrap() as u32 + 1)
}
#[inline]
pub fn prev_char(s: &str, mut i: usize) -> usize {
if i == 0 { return 0; }

View File

@ -145,11 +145,28 @@ fn struct_lits() {
let x = Bar;
// Comment
let y = Foo { a: x };
Foo { a: foo(), b: bar(), ..something };
Foo { a: foo(), // comment
// comment
b: bar(),
..something };
Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(),
b: bar(), };
Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(),
b: bar(), };
Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { // 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() };
}