Skip to content

Commit c3b22a2

Browse files
authored
Merge pull request #1829 from topecongiro/issue-1046
Allow attributes to stay on the same line with fields
2 parents 0ee76be + a763f3b commit c3b22a2

16 files changed

+278
-218
lines changed

Cargo.lock

Lines changed: 15 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/comment.rs

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use {Indent, Shape};
1818
use config::Config;
1919
use rewrite::RewriteContext;
2020
use string::{rewrite_string, StringFormat};
21-
use utils::wrap_str;
21+
use utils::{first_line_width, last_line_width, wrap_str};
2222

2323
fn is_custom_comment(comment: &str) -> bool {
2424
if !comment.starts_with("//") {
@@ -136,6 +136,93 @@ fn comment_style(orig: &str, normalize_comments: bool) -> CommentStyle {
136136
}
137137
}
138138

139+
pub fn combine_strs_with_missing_comments(
140+
context: &RewriteContext,
141+
prev_str: &str,
142+
next_str: &str,
143+
span: Span,
144+
shape: Shape,
145+
allow_extend: bool,
146+
) -> Option<String> {
147+
let mut allow_one_line = !prev_str.contains('\n') && !next_str.contains('\n');
148+
let first_sep = if prev_str.is_empty() || next_str.is_empty() {
149+
""
150+
} else {
151+
" "
152+
};
153+
let mut one_line_width =
154+
last_line_width(prev_str) + first_line_width(next_str) + first_sep.len();
155+
156+
let original_snippet = context.snippet(span);
157+
let trimmed_snippet = original_snippet.trim();
158+
let indent_str = shape.indent.to_string(context.config);
159+
160+
if trimmed_snippet.is_empty() {
161+
if allow_extend && prev_str.len() + first_sep.len() + next_str.len() <= shape.width {
162+
return Some(format!("{}{}{}", prev_str, first_sep, next_str));
163+
} else {
164+
let sep = if prev_str.is_empty() {
165+
String::new()
166+
} else {
167+
String::from("\n") + &indent_str
168+
};
169+
return Some(format!("{}{}{}", prev_str, sep, next_str));
170+
}
171+
}
172+
173+
// We have a missing comment between the first expression and the second expression.
174+
175+
// Peek the the original source code and find out whether there is a newline between the first
176+
// expression and the second expression or the missing comment. We will preserve the orginal
177+
// layout whenever possible.
178+
let prefer_same_line = if let Some(pos) = original_snippet.chars().position(|c| c == '/') {
179+
!original_snippet[..pos].contains('\n')
180+
} else {
181+
!original_snippet.contains('\n')
182+
};
183+
184+
let missing_comment = try_opt!(rewrite_comment(
185+
trimmed_snippet,
186+
false,
187+
shape,
188+
context.config
189+
));
190+
one_line_width -= first_sep.len();
191+
let first_sep = if prev_str.is_empty() || missing_comment.is_empty() {
192+
String::new()
193+
} else {
194+
let one_line_width = last_line_width(prev_str) + first_line_width(&missing_comment) + 1;
195+
if prefer_same_line && one_line_width <= shape.width {
196+
String::from(" ")
197+
} else {
198+
format!("\n{}", indent_str)
199+
}
200+
};
201+
let second_sep = if missing_comment.is_empty() || next_str.is_empty() {
202+
String::new()
203+
} else {
204+
if missing_comment.starts_with("//") {
205+
format!("\n{}", indent_str)
206+
} else {
207+
one_line_width += missing_comment.len() + first_sep.len() + 1;
208+
allow_one_line &= !missing_comment.starts_with("//") && !missing_comment.contains('\n');
209+
if prefer_same_line && allow_one_line && one_line_width <= shape.width {
210+
String::from(" ")
211+
} else {
212+
format!("\n{}", indent_str)
213+
}
214+
}
215+
};
216+
Some(format!(
217+
"{}{}{}{}{}",
218+
prev_str,
219+
first_sep,
220+
missing_comment,
221+
second_sep,
222+
next_str,
223+
))
224+
}
225+
139226
pub fn rewrite_comment(
140227
orig: &str,
141228
block_style: bool,

src/expr.rs

Lines changed: 9 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ use syntax::parse::classify;
1919
use {Indent, Shape, Spanned};
2020
use chains::rewrite_chain;
2121
use codemap::{LineRangeUtils, SpanUtils};
22-
use comment::{contains_comment, recover_comment_removed, rewrite_comment, FindUncommented};
22+
use comment::{combine_strs_with_missing_comments, contains_comment, recover_comment_removed,
23+
rewrite_comment, FindUncommented};
2324
use config::{Config, ControlBraceStyle, IndentStyle, MultilineStyle, Style};
2425
use items::{span_hi_for_arg, span_lo_for_arg};
2526
use lists::{definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting,
@@ -49,61 +50,6 @@ pub enum ExprType {
4950
SubExpression,
5051
}
5152

52-
fn combine_attr_and_expr(
53-
context: &RewriteContext,
54-
shape: Shape,
55-
expr: &ast::Expr,
56-
expr_str: &str,
57-
) -> Option<String> {
58-
let attrs = outer_attributes(&expr.attrs);
59-
let attr_str = try_opt!(attrs.rewrite(context, shape));
60-
let separator = if attr_str.is_empty() {
61-
String::new()
62-
} else {
63-
// Try to recover comments between the attributes and the expression if available.
64-
let missing_snippet = context.snippet(mk_sp(attrs[attrs.len() - 1].span.hi, expr.span.lo));
65-
let comment_opening_pos = missing_snippet.chars().position(|c| c == '/');
66-
let prefer_same_line = if let Some(pos) = comment_opening_pos {
67-
!missing_snippet[..pos].contains('\n')
68-
} else {
69-
!missing_snippet.contains('\n')
70-
};
71-
72-
let trimmed = missing_snippet.trim();
73-
let missing_comment = if trimmed.is_empty() {
74-
String::new()
75-
} else {
76-
try_opt!(rewrite_comment(&trimmed, false, shape, context.config))
77-
};
78-
79-
// 2 = ` ` + ` `
80-
let one_line_width =
81-
attr_str.len() + missing_comment.len() + 2 + first_line_width(expr_str);
82-
let attr_expr_separator = if prefer_same_line && !missing_comment.starts_with("//") &&
83-
one_line_width <= shape.width
84-
{
85-
String::from(" ")
86-
} else {
87-
format!("\n{}", shape.indent.to_string(context.config))
88-
};
89-
90-
if missing_comment.is_empty() {
91-
attr_expr_separator
92-
} else {
93-
// 1 = ` `
94-
let one_line_width =
95-
last_line_width(&attr_str) + 1 + first_line_width(&missing_comment);
96-
let attr_comment_separator = if prefer_same_line && one_line_width <= shape.width {
97-
String::from(" ")
98-
} else {
99-
format!("\n{}", shape.indent.to_string(context.config))
100-
};
101-
attr_comment_separator + &missing_comment + &attr_expr_separator
102-
}
103-
};
104-
Some(format!("{}{}{}", attr_str, separator, expr_str))
105-
}
106-
10753
pub fn format_expr(
10854
expr: &ast::Expr,
10955
expr_type: ExprType,
@@ -355,7 +301,13 @@ pub fn format_expr(
355301
recover_comment_removed(expr_str, expr.span, context, shape)
356302
})
357303
.and_then(|expr_str| {
358-
combine_attr_and_expr(context, shape, expr, &expr_str)
304+
let attrs = outer_attributes(&expr.attrs);
305+
let attrs_str = try_opt!(attrs.rewrite(context, shape));
306+
let span = mk_sp(
307+
attrs.last().map_or(expr.span.lo, |attr| attr.span.hi),
308+
expr.span.lo,
309+
);
310+
combine_strs_with_missing_comments(context, &attrs_str, &expr_str, span, shape, false)
359311
})
360312
}
361313

0 commit comments

Comments
 (0)