Format tuple-like structs
This commit is contained in:
parent
66c6fe5334
commit
0ef5db9496
22 changed files with 1075 additions and 514 deletions
202
src/comment.rs
Normal file
202
src/comment.rs
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// Format comments.
|
||||||
|
|
||||||
|
use string::{StringFormat, rewrite_string};
|
||||||
|
use utils::make_indent;
|
||||||
|
|
||||||
|
pub fn rewrite_comment(orig: &str, block_style: bool, width: usize, offset: usize) -> String {
|
||||||
|
let s = orig.trim();
|
||||||
|
|
||||||
|
// Edge case: block comments. Let's not trim their lines (for now).
|
||||||
|
let opener = if block_style { "/* " } else { "// " };
|
||||||
|
let closer = if block_style { " */" } else { "" };
|
||||||
|
let line_start = if block_style { " * " } else { "// " };
|
||||||
|
|
||||||
|
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 indent_str = make_indent(offset);
|
||||||
|
let line_breaks = s.chars().filter(|&c| c == '\n').count();
|
||||||
|
|
||||||
|
let (_, mut s) = s.lines().enumerate()
|
||||||
|
.map(|(i, mut line)| {
|
||||||
|
line = line.trim();
|
||||||
|
|
||||||
|
// Drop old closer.
|
||||||
|
if i == line_breaks && line.ends_with("*/") && !line.starts_with("//") {
|
||||||
|
line = &line[..(line.len() - 2)];
|
||||||
|
}
|
||||||
|
|
||||||
|
line.trim_right_matches(' ')
|
||||||
|
})
|
||||||
|
.map(left_trim_comment_line)
|
||||||
|
.fold((true, opener.to_owned()), |(first, mut acc), line| {
|
||||||
|
if !first {
|
||||||
|
acc.push('\n');
|
||||||
|
acc.push_str(&indent_str);
|
||||||
|
acc.push_str(line_start);
|
||||||
|
}
|
||||||
|
|
||||||
|
if line.len() > max_chars {
|
||||||
|
acc.push_str(&rewrite_string(line, &fmt));
|
||||||
|
} else {
|
||||||
|
acc.push_str(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
(false, acc)
|
||||||
|
});
|
||||||
|
|
||||||
|
s.push_str(closer);
|
||||||
|
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
fn left_trim_comment_line<'a>(line: &'a str) -> &'a str {
|
||||||
|
if line.starts_with("/* ") || line.starts_with("// ") {
|
||||||
|
&line[3..]
|
||||||
|
} else if line.starts_with("/*") || line.starts_with("* ") || line.starts_with("//") {
|
||||||
|
&line[2..]
|
||||||
|
} else if line.starts_with("*") {
|
||||||
|
&line[1..]
|
||||||
|
} else {
|
||||||
|
line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn format_comments() {
|
||||||
|
assert_eq!("/* test */", rewrite_comment(" //test", true, 100, 100));
|
||||||
|
assert_eq!("// comment\n// on a", rewrite_comment("// comment on a", false, 10, 0));
|
||||||
|
|
||||||
|
assert_eq!("// A multi line comment\n // between args.",
|
||||||
|
rewrite_comment("// A multi line comment\n // between args.",
|
||||||
|
false,
|
||||||
|
60,
|
||||||
|
12));
|
||||||
|
|
||||||
|
let input = "// comment";
|
||||||
|
let expected_output = "/* com\n \
|
||||||
|
* men\n \
|
||||||
|
* t */";
|
||||||
|
assert_eq!(expected_output, rewrite_comment(input, true, 9, 69));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub trait FindUncommented {
|
||||||
|
fn find_uncommented(&self, pat: &str) -> Option<usize>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FindUncommented for str {
|
||||||
|
fn find_uncommented(&self, pat: &str) -> Option<usize> {
|
||||||
|
let mut needle_iter = pat.chars();
|
||||||
|
let mut possible_comment = false;
|
||||||
|
|
||||||
|
for (i, b) in self.char_indices() {
|
||||||
|
match needle_iter.next() {
|
||||||
|
Some(c) => {
|
||||||
|
if b != c {
|
||||||
|
needle_iter = pat.chars();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => return Some(i - pat.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
if possible_comment && (b == '/' || b == '*') {
|
||||||
|
return find_comment_end(&self[(i-1)..])
|
||||||
|
.and_then(|end| {
|
||||||
|
self[(end + i - 1)..].find_uncommented(pat)
|
||||||
|
.map(|idx| idx + end + i - 1)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
possible_comment = b == '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle case where the pattern is a suffix of the search string
|
||||||
|
match needle_iter.next() {
|
||||||
|
Some(_) => None,
|
||||||
|
None => Some(self.len() - pat.len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_uncommented() {
|
||||||
|
fn check(haystack: &str, needle: &str, expected: Option<usize>) {
|
||||||
|
println!("haystack {:?}, needle: {:?}", haystack, needle);
|
||||||
|
assert_eq!(expected, haystack.find_uncommented(needle));
|
||||||
|
}
|
||||||
|
|
||||||
|
check("/*/ */test", "test", Some(6));
|
||||||
|
check("//test\ntest", "test", Some(7));
|
||||||
|
check("/* comment only */", "whatever", None);
|
||||||
|
check("/* comment */ some text /* more commentary */ result", "result", Some(46));
|
||||||
|
check("sup // sup", "p", Some(2));
|
||||||
|
check("sup", "x", None);
|
||||||
|
check("π? /**/ π is nice!", "π is nice", Some(9));
|
||||||
|
check("/*sup yo? \n sup*/ sup", "p", Some(20));
|
||||||
|
check("hel/*lohello*/lo", "hello", None);
|
||||||
|
check("acb", "ab", None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the first byte position after the first comment. The given string
|
||||||
|
// is expected to be prefixed by a comment, including delimiters.
|
||||||
|
// Good: "/* /* inner */ outer */ code();"
|
||||||
|
// Bad: "code(); // hello\n world!"
|
||||||
|
pub fn find_comment_end(s: &str) -> Option<usize> {
|
||||||
|
if s.starts_with("//") {
|
||||||
|
s.find('\n').map(|idx| idx + 1)
|
||||||
|
} else {
|
||||||
|
// Block comment
|
||||||
|
let mut levels = 0;
|
||||||
|
let mut prev_char = 'a';
|
||||||
|
|
||||||
|
for (i, mut c) in s.char_indices() {
|
||||||
|
if c == '*' && prev_char == '/' {
|
||||||
|
levels += 1;
|
||||||
|
c = 'a'; // Invalidate prev_char
|
||||||
|
} else if c == '/' && prev_char == '*' {
|
||||||
|
levels -= 1;
|
||||||
|
|
||||||
|
if levels == 0 {
|
||||||
|
return Some(i + 1);
|
||||||
|
}
|
||||||
|
c = 'a';
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_char = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn comment_end() {
|
||||||
|
assert_eq!(Some(6), find_comment_end("// hi\n"));
|
||||||
|
assert_eq!(Some(9), find_comment_end("/* sup */ "));
|
||||||
|
assert_eq!(Some(9), find_comment_end("/*/**/ */ "));
|
||||||
|
assert_eq!(Some(6), find_comment_end("/*/ */ weird!"));
|
||||||
|
assert_eq!(None, find_comment_end("/* hi /* test */"));
|
||||||
|
assert_eq!(None, find_comment_end("// hi /* test */"));
|
||||||
|
assert_eq!(Some(9), find_comment_end("// hi /*\n."));
|
||||||
|
}
|
|
@ -24,7 +24,7 @@ pub struct Config {
|
||||||
pub fn_brace_style: BraceStyle,
|
pub fn_brace_style: BraceStyle,
|
||||||
pub fn_return_indent: ReturnIndent,
|
pub fn_return_indent: ReturnIndent,
|
||||||
pub fn_args_paren_newline: bool,
|
pub fn_args_paren_newline: bool,
|
||||||
pub struct_trailing_comma: bool,
|
pub struct_trailing_comma: SeparatorTactic,
|
||||||
pub struct_lit_trailing_comma: SeparatorTactic,
|
pub struct_lit_trailing_comma: SeparatorTactic,
|
||||||
pub enum_trailing_comma: bool,
|
pub enum_trailing_comma: bool,
|
||||||
pub report_todo: ReportTactic,
|
pub report_todo: ReportTactic,
|
||||||
|
|
|
@ -6,7 +6,7 @@ newline_style = "Unix"
|
||||||
fn_brace_style = "SameLineWhere"
|
fn_brace_style = "SameLineWhere"
|
||||||
fn_return_indent = "WithArgs"
|
fn_return_indent = "WithArgs"
|
||||||
fn_args_paren_newline = true
|
fn_args_paren_newline = true
|
||||||
struct_trailing_comma = true
|
struct_trailing_comma = "Vertical"
|
||||||
struct_lit_trailing_comma = "Vertical"
|
struct_lit_trailing_comma = "Vertical"
|
||||||
enum_trailing_comma = true
|
enum_trailing_comma = true
|
||||||
report_todo = "Always"
|
report_todo = "Always"
|
||||||
|
|
223
src/expr.rs
223
src/expr.rs
|
@ -8,17 +8,15 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use utils::*;
|
|
||||||
use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};
|
|
||||||
use rewrite::{Rewrite, RewriteContext};
|
use rewrite::{Rewrite, RewriteContext};
|
||||||
|
use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic, ListTactic};
|
||||||
|
use string::{StringFormat, rewrite_string};
|
||||||
|
|
||||||
use syntax::{ast, ptr};
|
use syntax::{ast, ptr};
|
||||||
use syntax::codemap::{Pos, Span};
|
use syntax::codemap::{Pos, Span, BytePos};
|
||||||
use syntax::parse::token;
|
use syntax::parse::token;
|
||||||
use syntax::print::pprust;
|
use syntax::print::pprust;
|
||||||
|
|
||||||
use MIN_STRING;
|
|
||||||
|
|
||||||
impl Rewrite for ast::Expr {
|
impl Rewrite for ast::Expr {
|
||||||
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
|
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
|
||||||
match self.node {
|
match self.node {
|
||||||
|
@ -33,7 +31,7 @@ impl Rewrite for ast::Expr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Expr_::ExprCall(ref callee, ref args) => {
|
ast::Expr_::ExprCall(ref callee, ref args) => {
|
||||||
return rewrite_call(context, callee, args, width, offset);
|
return rewrite_call(context, callee, args, self.span, width, offset);
|
||||||
}
|
}
|
||||||
ast::Expr_::ExprParen(ref subexpr) => {
|
ast::Expr_::ExprParen(ref subexpr) => {
|
||||||
return rewrite_paren(context, subexpr, width, offset);
|
return rewrite_paren(context, subexpr, width, offset);
|
||||||
|
@ -46,7 +44,7 @@ impl Rewrite for ast::Expr {
|
||||||
offset);
|
offset);
|
||||||
}
|
}
|
||||||
ast::Expr_::ExprTup(ref items) => {
|
ast::Expr_::ExprTup(ref items) => {
|
||||||
return rewrite_tuple_lit(context, items, width, offset);
|
return rewrite_tuple_lit(context, items, self.span, width, offset);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -55,9 +53,12 @@ impl Rewrite for ast::Expr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rewrite_string_lit(context: &RewriteContext, s: &str, span: Span, width: usize, offset: usize) -> Option<String> {
|
fn rewrite_string_lit(context: &RewriteContext,
|
||||||
// FIXME I bet this stomps unicode escapes in the source string
|
s: &str,
|
||||||
|
span: Span,
|
||||||
|
width: usize,
|
||||||
|
offset: usize)
|
||||||
|
-> Option<String> {
|
||||||
// Check if there is anything to fix: we always try to fixup multi-line
|
// Check if there is anything to fix: we always try to fixup multi-line
|
||||||
// strings, or if the string is too long for the line.
|
// strings, or if the string is too long for the line.
|
||||||
let l_loc = context.codemap.lookup_char_pos(span.lo);
|
let l_loc = context.codemap.lookup_char_pos(span.lo);
|
||||||
|
@ -65,102 +66,63 @@ fn rewrite_string_lit(context: &RewriteContext, s: &str, span: Span, width: usiz
|
||||||
if l_loc.line == r_loc.line && r_loc.col.to_usize() <= context.config.max_width {
|
if l_loc.line == r_loc.line && r_loc.col.to_usize() <= context.config.max_width {
|
||||||
return context.codemap.span_to_snippet(span).ok();
|
return context.codemap.span_to_snippet(span).ok();
|
||||||
}
|
}
|
||||||
|
let fmt = StringFormat {
|
||||||
|
opener: "\"",
|
||||||
|
closer: "\"",
|
||||||
|
line_start: " ",
|
||||||
|
line_end: "\\",
|
||||||
|
width: width,
|
||||||
|
offset: offset,
|
||||||
|
trim_end: false
|
||||||
|
};
|
||||||
|
|
||||||
// TODO if lo.col > IDEAL - 10, start a new line (need cur indent for that)
|
Some(rewrite_string(&s.escape_default(), &fmt))
|
||||||
|
|
||||||
let s = s.escape_default();
|
|
||||||
|
|
||||||
let offset = offset + 1;
|
|
||||||
let indent = make_indent(offset);
|
|
||||||
let indent = &indent;
|
|
||||||
|
|
||||||
let mut cur_start = 0;
|
|
||||||
let mut result = String::with_capacity(round_up_to_power_of_two(s.len()));
|
|
||||||
result.push('"');
|
|
||||||
loop {
|
|
||||||
let max_chars = if cur_start == 0 {
|
|
||||||
// First line.
|
|
||||||
width - 2 // 2 = " + \
|
|
||||||
} else {
|
|
||||||
context.config.max_width - offset - 1 // 1 = either \ or ;
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut cur_end = cur_start + max_chars;
|
|
||||||
|
|
||||||
if cur_end >= s.len() {
|
|
||||||
result.push_str(&s[cur_start..]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure we're on a char boundary.
|
|
||||||
cur_end = next_char(&s, cur_end);
|
|
||||||
|
|
||||||
// Push cur_end left until we reach whitespace
|
|
||||||
while !s.char_at(cur_end-1).is_whitespace() {
|
|
||||||
cur_end = prev_char(&s, cur_end);
|
|
||||||
|
|
||||||
if cur_end - cur_start < MIN_STRING {
|
|
||||||
// We can't break at whitespace, fall back to splitting
|
|
||||||
// anywhere that doesn't break an escape sequence
|
|
||||||
cur_end = next_char(&s, cur_start + max_chars);
|
|
||||||
while s.char_at(prev_char(&s, cur_end)) == '\\' {
|
|
||||||
cur_end = prev_char(&s, cur_end);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Make sure there is no whitespace to the right of the break.
|
|
||||||
while cur_end < s.len() && s.char_at(cur_end).is_whitespace() {
|
|
||||||
cur_end = next_char(&s, cur_end+1);
|
|
||||||
}
|
|
||||||
result.push_str(&s[cur_start..cur_end]);
|
|
||||||
result.push_str("\\\n");
|
|
||||||
result.push_str(indent);
|
|
||||||
|
|
||||||
cur_start = cur_end;
|
|
||||||
}
|
|
||||||
result.push('"');
|
|
||||||
|
|
||||||
Some(result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rewrite_call(context: &RewriteContext,
|
fn rewrite_call(context: &RewriteContext,
|
||||||
callee: &ast::Expr,
|
callee: &ast::Expr,
|
||||||
args: &[ptr::P<ast::Expr>],
|
args: &[ptr::P<ast::Expr>],
|
||||||
|
span: Span,
|
||||||
width: usize,
|
width: usize,
|
||||||
offset: usize)
|
offset: usize)
|
||||||
-> Option<String>
|
-> Option<String> {
|
||||||
{
|
|
||||||
debug!("rewrite_call, width: {}, offset: {}", width, offset);
|
debug!("rewrite_call, width: {}, offset: {}", width, offset);
|
||||||
|
|
||||||
// TODO using byte lens instead of char lens (and probably all over the place too)
|
// TODO using byte lens instead of char lens (and probably all over the place too)
|
||||||
let callee_str = try_opt!(callee.rewrite(context, width, offset));
|
let callee_str = try_opt!(callee.rewrite(context, width, offset));
|
||||||
debug!("rewrite_call, callee_str: `{:?}`", callee_str);
|
debug!("rewrite_call, callee_str: `{}`", callee_str);
|
||||||
|
|
||||||
|
if args.len() == 0 {
|
||||||
|
return Some(format!("{}()", callee_str));
|
||||||
|
}
|
||||||
|
|
||||||
// 2 is for parens.
|
// 2 is for parens.
|
||||||
let remaining_width = width - callee_str.len() - 2;
|
let remaining_width = width - callee_str.len() - 2;
|
||||||
let offset = callee_str.len() + 1 + offset;
|
let offset = callee_str.len() + 1 + offset;
|
||||||
let arg_count = args.len();
|
|
||||||
|
|
||||||
let args_str = if arg_count > 0 {
|
let items = itemize_list(context.codemap,
|
||||||
let args_rewritten: Vec<_> =
|
Vec::new(),
|
||||||
try_opt!(args.iter()
|
args.iter(),
|
||||||
.map(|arg| arg.rewrite(context, remaining_width, offset)
|
",",
|
||||||
.map(|arg_str| (arg_str, String::new())))
|
")",
|
||||||
.collect());
|
|item| item.span.lo,
|
||||||
let fmt = ListFormatting {
|
|item| item.span.hi,
|
||||||
tactic: ListTactic::HorizontalVertical,
|
|item| item.rewrite(context, remaining_width, offset)
|
||||||
separator: ",",
|
.unwrap(), // FIXME: don't unwrap, take span literal
|
||||||
trailing_separator: SeparatorTactic::Never,
|
callee.span.hi + BytePos(1),
|
||||||
indent: offset,
|
span.hi);
|
||||||
h_width: remaining_width,
|
|
||||||
v_width: remaining_width,
|
let fmt = ListFormatting {
|
||||||
};
|
tactic: ListTactic::HorizontalVertical,
|
||||||
write_list(&args_rewritten, &fmt)
|
separator: ",",
|
||||||
} else {
|
trailing_separator: SeparatorTactic::Never,
|
||||||
String::new()
|
indent: offset,
|
||||||
|
h_width: remaining_width,
|
||||||
|
v_width: remaining_width,
|
||||||
|
is_expression: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(format!("{}({})", callee_str, args_str))
|
Some(format!("{}({})", callee_str, write_list(&items, &fmt)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rewrite_paren(context: &RewriteContext, subexpr: &ast::Expr, width: usize, offset: usize) -> Option<String> {
|
fn rewrite_paren(context: &RewriteContext, subexpr: &ast::Expr, width: usize, offset: usize) -> Option<String> {
|
||||||
|
@ -198,8 +160,9 @@ fn rewrite_struct_lit(context: &RewriteContext,
|
||||||
indent + 2)
|
indent + 2)
|
||||||
.map(|s| format!("..{}", s))))
|
.map(|s| format!("..{}", s))))
|
||||||
.collect());
|
.collect());
|
||||||
|
|
||||||
// FIXME comments
|
// FIXME comments
|
||||||
let field_strs: Vec<_> = field_strs.into_iter().map(|s| (s, String::new())).collect();
|
let field_strs: Vec<_> = field_strs.into_iter().map(ListItem::from_str).collect();
|
||||||
let fmt = ListFormatting {
|
let fmt = ListFormatting {
|
||||||
tactic: ListTactic::HorizontalVertical,
|
tactic: ListTactic::HorizontalVertical,
|
||||||
separator: ",",
|
separator: ",",
|
||||||
|
@ -211,14 +174,15 @@ fn rewrite_struct_lit(context: &RewriteContext,
|
||||||
indent: indent,
|
indent: indent,
|
||||||
h_width: budget,
|
h_width: budget,
|
||||||
v_width: budget,
|
v_width: budget,
|
||||||
|
is_expression: true,
|
||||||
};
|
};
|
||||||
let fields_str = write_list(&field_strs, &fmt);
|
let fields_str = write_list(&field_strs, &fmt);
|
||||||
Some(format!("{} {{ {} }}", path_str, fields_str))
|
Some(format!("{} {{ {} }}", path_str, fields_str))
|
||||||
|
|
||||||
// FIXME if the usual multi-line layout is too wide, we should fall back to
|
// FIXME if the usual multi-line layout is too wide, we should fall back to
|
||||||
// Foo {
|
// Foo {
|
||||||
// a: ...,
|
// a: ...,
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rewrite_field(context: &RewriteContext, field: &ast::Field, width: usize, offset: usize) -> Option<String> {
|
fn rewrite_field(context: &RewriteContext, field: &ast::Field, width: usize, offset: usize) -> Option<String> {
|
||||||
|
@ -230,43 +194,42 @@ fn rewrite_field(context: &RewriteContext, field: &ast::Field, width: usize, off
|
||||||
|
|
||||||
fn rewrite_tuple_lit(context: &RewriteContext,
|
fn rewrite_tuple_lit(context: &RewriteContext,
|
||||||
items: &[ptr::P<ast::Expr>],
|
items: &[ptr::P<ast::Expr>],
|
||||||
|
span: Span,
|
||||||
width: usize,
|
width: usize,
|
||||||
offset: usize)
|
offset: usize)
|
||||||
-> Option<String> {
|
-> Option<String> {
|
||||||
// opening paren
|
let indent = offset + 1;
|
||||||
let indent = offset + 1;
|
|
||||||
// In case of length 1, need a trailing comma
|
let items = itemize_list(context.codemap,
|
||||||
if items.len() == 1 {
|
Vec::new(),
|
||||||
return items[0].rewrite(context, width - 3, indent).map(|s| format!("({},)", s));
|
items.into_iter(),
|
||||||
}
|
",",
|
||||||
// Only last line has width-1 as budget, other may take max_width
|
")",
|
||||||
let item_strs: Vec<_> =
|
|item| item.span.lo,
|
||||||
try_opt!(items.iter()
|
|item| item.span.hi,
|
||||||
.enumerate()
|
|item| item.rewrite(context,
|
||||||
.map(|(i, item)| {
|
context.config.max_width - indent - 2,
|
||||||
let rem_width = if i == items.len() - 1 {
|
indent)
|
||||||
width - 2
|
.unwrap(), // FIXME: don't unwrap, take span literal
|
||||||
} else {
|
span.lo + BytePos(1), // Remove parens
|
||||||
context.config.max_width - indent - 2
|
span.hi - BytePos(1));
|
||||||
};
|
|
||||||
item.rewrite(context, rem_width, indent)
|
// In case of length 1, need a trailing comma
|
||||||
})
|
let trailing_separator_tactic = if items.len() == 1 {
|
||||||
.collect());
|
SeparatorTactic::Always
|
||||||
let tactics = if item_strs.iter().any(|s| s.contains('\n')) {
|
} else {
|
||||||
ListTactic::Vertical
|
SeparatorTactic::Never
|
||||||
} else {
|
};
|
||||||
ListTactic::HorizontalVertical
|
|
||||||
};
|
let fmt = ListFormatting {
|
||||||
// FIXME handle comments
|
tactic: ListTactic::HorizontalVertical,
|
||||||
let item_strs: Vec<_> = item_strs.into_iter().map(|s| (s, String::new())).collect();
|
separator: ",",
|
||||||
let fmt = ListFormatting {
|
trailing_separator: trailing_separator_tactic,
|
||||||
tactic: tactics,
|
indent: indent,
|
||||||
separator: ",",
|
h_width: width - 2,
|
||||||
trailing_separator: SeparatorTactic::Never,
|
v_width: width - 2,
|
||||||
indent: indent,
|
is_expression: true,
|
||||||
h_width: width - 2,
|
};
|
||||||
v_width: width - 2,
|
|
||||||
};
|
Some(format!("({})", write_list(&items, &fmt)))
|
||||||
let item_str = write_list(&item_strs, &fmt);
|
}
|
||||||
Some(format!("({})", item_str))
|
|
||||||
}
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use visitor::FmtVisitor;
|
use visitor::FmtVisitor;
|
||||||
use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};
|
use lists::{write_list, ListItem, ListFormatting, SeparatorTactic, ListTactic};
|
||||||
use utils::format_visibility;
|
use utils::format_visibility;
|
||||||
|
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
|
@ -65,16 +65,8 @@ impl<'a> FmtVisitor<'a> {
|
||||||
let used_width = indent + 2;
|
let used_width = indent + 2;
|
||||||
|
|
||||||
// Break as early as possible when we've blown our budget.
|
// Break as early as possible when we've blown our budget.
|
||||||
let remaining_line_budget = if used_width > one_line_budget {
|
let remaining_line_budget = one_line_budget.checked_sub(used_width).unwrap_or(0);
|
||||||
0
|
let remaining_multi_budget = multi_line_budget.checked_sub(used_width).unwrap_or(0);
|
||||||
} else {
|
|
||||||
one_line_budget - used_width
|
|
||||||
};
|
|
||||||
let remaining_multi_budget = if used_width > multi_line_budget {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
multi_line_budget - used_width
|
|
||||||
};
|
|
||||||
|
|
||||||
let fmt = ListFormatting {
|
let fmt = ListFormatting {
|
||||||
tactic: ListTactic::Mixed,
|
tactic: ListTactic::Mixed,
|
||||||
|
@ -83,6 +75,7 @@ impl<'a> FmtVisitor<'a> {
|
||||||
indent: block_indent + indent,
|
indent: block_indent + indent,
|
||||||
h_width: remaining_line_budget,
|
h_width: remaining_line_budget,
|
||||||
v_width: remaining_multi_budget,
|
v_width: remaining_multi_budget,
|
||||||
|
is_expression: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO handle any comments inbetween items.
|
// TODO handle any comments inbetween items.
|
||||||
|
@ -94,7 +87,7 @@ impl<'a> FmtVisitor<'a> {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Some(("self".to_owned(), String::new()))
|
Some(ListItem::from_str("self"))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -102,12 +95,13 @@ impl<'a> FmtVisitor<'a> {
|
||||||
let items: Vec<_> = head.into_iter().chain(path_list.iter().filter_map(|vpi| {
|
let items: Vec<_> = head.into_iter().chain(path_list.iter().filter_map(|vpi| {
|
||||||
match vpi.node {
|
match vpi.node {
|
||||||
ast::PathListItem_::PathListIdent{ name, .. } => {
|
ast::PathListItem_::PathListIdent{ name, .. } => {
|
||||||
Some((token::get_ident(name).to_string(), String::new()))
|
Some(ListItem::from_str(token::get_ident(name).to_string()))
|
||||||
}
|
}
|
||||||
// Skip `self`, because we added it above.
|
// Skip `self`, because we added it above.
|
||||||
ast::PathListItem_::PathListMod{ .. } => None,
|
ast::PathListItem_::PathListMod{ .. } => None,
|
||||||
}
|
}
|
||||||
})).collect();
|
})).collect();
|
||||||
|
|
||||||
Some(if path_str.len() == 0 {
|
Some(if path_str.len() == 0 {
|
||||||
format!("{}use {{{}}};", vis, write_list(&items, &fmt))
|
format!("{}use {{{}}};", vis, write_list(&items, &fmt))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -223,7 +223,6 @@ impl BadIssueSeeker {
|
||||||
#[test]
|
#[test]
|
||||||
fn find_unnumbered_issue() {
|
fn find_unnumbered_issue() {
|
||||||
fn check_fail(text: &str, failing_pos: usize) {
|
fn check_fail(text: &str, failing_pos: usize) {
|
||||||
println!("{:?}", text);
|
|
||||||
let mut seeker = BadIssueSeeker::new(ReportTactic::Unnumbered, ReportTactic::Unnumbered);
|
let mut seeker = BadIssueSeeker::new(ReportTactic::Unnumbered, ReportTactic::Unnumbered);
|
||||||
assert_eq!(Some(failing_pos), text.chars().position(|c| seeker.inspect(c).is_some()));
|
assert_eq!(Some(failing_pos), text.chars().position(|c| seeker.inspect(c).is_some()));
|
||||||
}
|
}
|
||||||
|
|
514
src/items.rs
514
src/items.rs
|
@ -11,9 +11,11 @@
|
||||||
// Formatting top-level items - functions, structs, enums, traits, impls.
|
// Formatting top-level items - functions, structs, enums, traits, impls.
|
||||||
|
|
||||||
use {ReturnIndent, BraceStyle};
|
use {ReturnIndent, BraceStyle};
|
||||||
use utils::{format_visibility, make_indent, FindUncommented};
|
use utils::{format_visibility, make_indent, contains_skip};
|
||||||
use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};
|
use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic, ListTactic};
|
||||||
|
use comment::FindUncommented;
|
||||||
use visitor::FmtVisitor;
|
use visitor::FmtVisitor;
|
||||||
|
|
||||||
use syntax::{ast, abi};
|
use syntax::{ast, abi};
|
||||||
use syntax::codemap::{self, Span, BytePos};
|
use syntax::codemap::{self, Span, BytePos};
|
||||||
use syntax::print::pprust;
|
use syntax::print::pprust;
|
||||||
|
@ -30,7 +32,7 @@ impl<'a> FmtVisitor<'a> {
|
||||||
constness: &ast::Constness,
|
constness: &ast::Constness,
|
||||||
abi: &abi::Abi,
|
abi: &abi::Abi,
|
||||||
vis: ast::Visibility,
|
vis: ast::Visibility,
|
||||||
span_end: BytePos)
|
span: Span)
|
||||||
-> String
|
-> String
|
||||||
{
|
{
|
||||||
let newline_brace = self.newline_for_brace(&generics.where_clause);
|
let newline_brace = self.newline_for_brace(&generics.where_clause);
|
||||||
|
@ -44,7 +46,7 @@ impl<'a> FmtVisitor<'a> {
|
||||||
constness,
|
constness,
|
||||||
abi,
|
abi,
|
||||||
vis,
|
vis,
|
||||||
span_end,
|
span,
|
||||||
newline_brace);
|
newline_brace);
|
||||||
|
|
||||||
// Prepare for the function body by possibly adding a newline and indent.
|
// Prepare for the function body by possibly adding a newline and indent.
|
||||||
|
@ -68,7 +70,7 @@ impl<'a> FmtVisitor<'a> {
|
||||||
-> String
|
-> String
|
||||||
{
|
{
|
||||||
// Drop semicolon or it will be interpreted as comment
|
// Drop semicolon or it will be interpreted as comment
|
||||||
let span_end = span.hi - BytePos(1);
|
let span = codemap::mk_sp(span.lo, span.hi - BytePos(1));
|
||||||
|
|
||||||
let mut result = self.rewrite_fn_base(indent,
|
let mut result = self.rewrite_fn_base(indent,
|
||||||
ident,
|
ident,
|
||||||
|
@ -79,7 +81,7 @@ impl<'a> FmtVisitor<'a> {
|
||||||
&sig.constness,
|
&sig.constness,
|
||||||
&sig.abi,
|
&sig.abi,
|
||||||
ast::Visibility::Inherited,
|
ast::Visibility::Inherited,
|
||||||
span_end,
|
span,
|
||||||
false);
|
false);
|
||||||
|
|
||||||
// Re-attach semicolon
|
// Re-attach semicolon
|
||||||
|
@ -98,7 +100,7 @@ impl<'a> FmtVisitor<'a> {
|
||||||
constness: &ast::Constness,
|
constness: &ast::Constness,
|
||||||
abi: &abi::Abi,
|
abi: &abi::Abi,
|
||||||
vis: ast::Visibility,
|
vis: ast::Visibility,
|
||||||
span_end: BytePos,
|
span: Span,
|
||||||
newline_brace: bool)
|
newline_brace: bool)
|
||||||
-> String
|
-> String
|
||||||
{
|
{
|
||||||
|
@ -131,7 +133,8 @@ impl<'a> FmtVisitor<'a> {
|
||||||
let generics_indent = indent + result.len();
|
let generics_indent = indent + result.len();
|
||||||
result.push_str(&self.rewrite_generics(generics,
|
result.push_str(&self.rewrite_generics(generics,
|
||||||
generics_indent,
|
generics_indent,
|
||||||
span_for_return(&fd.output).lo));
|
codemap::mk_sp(span.lo,
|
||||||
|
span_for_return(&fd.output).lo)));
|
||||||
|
|
||||||
let ret_str = self.rewrite_return(&fd.output);
|
let ret_str = self.rewrite_return(&fd.output);
|
||||||
|
|
||||||
|
@ -162,7 +165,8 @@ impl<'a> FmtVisitor<'a> {
|
||||||
one_line_budget,
|
one_line_budget,
|
||||||
multi_line_budget,
|
multi_line_budget,
|
||||||
arg_indent,
|
arg_indent,
|
||||||
span_for_return(&fd.output)));
|
codemap::mk_sp(self.span_after(span, "("),
|
||||||
|
span_for_return(&fd.output).lo)));
|
||||||
result.push(')');
|
result.push(')');
|
||||||
|
|
||||||
// Return type.
|
// Return type.
|
||||||
|
@ -189,7 +193,7 @@ impl<'a> FmtVisitor<'a> {
|
||||||
// Comment between return type and the end of the decl.
|
// Comment between return type and the end of the decl.
|
||||||
let snippet_lo = fd.output.span().hi;
|
let snippet_lo = fd.output.span().hi;
|
||||||
if where_clause.predicates.len() == 0 {
|
if where_clause.predicates.len() == 0 {
|
||||||
let snippet_hi = span_end;
|
let snippet_hi = span.hi;
|
||||||
let snippet = self.snippet(codemap::mk_sp(snippet_lo, snippet_hi));
|
let snippet = self.snippet(codemap::mk_sp(snippet_lo, snippet_hi));
|
||||||
let snippet = snippet.trim();
|
let snippet = snippet.trim();
|
||||||
if snippet.len() > 0 {
|
if snippet.len() > 0 {
|
||||||
|
@ -204,7 +208,9 @@ impl<'a> FmtVisitor<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Where clause.
|
// Where clause.
|
||||||
result.push_str(&self.rewrite_where_clause(where_clause, indent, span_end));
|
result.push_str(&self.rewrite_where_clause(where_clause,
|
||||||
|
indent,
|
||||||
|
span.hi));
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -215,7 +221,7 @@ impl<'a> FmtVisitor<'a> {
|
||||||
one_line_budget: usize,
|
one_line_budget: usize,
|
||||||
multi_line_budget: usize,
|
multi_line_budget: usize,
|
||||||
arg_indent: usize,
|
arg_indent: usize,
|
||||||
ret_span: Span)
|
span: Span)
|
||||||
-> String
|
-> String
|
||||||
{
|
{
|
||||||
let mut arg_item_strs: Vec<_> = args.iter().map(|a| self.rewrite_fn_input(a)).collect();
|
let mut arg_item_strs: Vec<_> = args.iter().map(|a| self.rewrite_fn_input(a)).collect();
|
||||||
|
@ -262,89 +268,50 @@ impl<'a> FmtVisitor<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comments between args
|
// Comments between args
|
||||||
let mut arg_comments = Vec::new();
|
let mut arg_items = Vec::new();
|
||||||
if min_args == 2 {
|
if min_args == 2 {
|
||||||
arg_comments.push("".to_owned());
|
arg_items.push(ListItem::from_str(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO if there are no args, there might still be a comment, but without
|
// TODO if there are no args, there might still be a comment, but without
|
||||||
// spans for the comment or parens, there is no chance of getting it right.
|
// spans for the comment or parens, there is no chance of getting it right.
|
||||||
// You also don't get to put a comment on self, unless it is explicit.
|
// You also don't get to put a comment on self, unless it is explicit.
|
||||||
if args.len() >= min_args {
|
if args.len() >= min_args {
|
||||||
arg_comments = self.make_comments_for_list(arg_comments,
|
let comment_span_start = if min_args == 2 {
|
||||||
args[min_args-1..].iter(),
|
self.span_after(span, ",")
|
||||||
",",
|
} else {
|
||||||
")",
|
span.lo
|
||||||
|arg| arg.pat.span.lo,
|
};
|
||||||
|arg| arg.ty.span.hi,
|
|
||||||
ret_span.lo);
|
arg_items = itemize_list(self.codemap,
|
||||||
|
arg_items,
|
||||||
|
args[min_args-1..].iter(),
|
||||||
|
",",
|
||||||
|
")",
|
||||||
|
|arg| arg.pat.span.lo,
|
||||||
|
|arg| arg.ty.span.hi,
|
||||||
|
|_| String::new(),
|
||||||
|
comment_span_start,
|
||||||
|
span.hi);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("comments: {:?}", arg_comments);
|
assert_eq!(arg_item_strs.len(), arg_items.len());
|
||||||
|
|
||||||
// If there are // comments, keep them multi-line.
|
for (item, arg) in arg_items.iter_mut().zip(arg_item_strs) {
|
||||||
let mut list_tactic = ListTactic::HorizontalVertical;
|
item.item = arg;
|
||||||
if arg_comments.iter().any(|c| c.contains("//")) {
|
|
||||||
list_tactic = ListTactic::Vertical;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(arg_item_strs.len(), arg_comments.len());
|
|
||||||
let arg_strs: Vec<_> = arg_item_strs.into_iter().zip(arg_comments.into_iter()).collect();
|
|
||||||
|
|
||||||
let fmt = ListFormatting {
|
let fmt = ListFormatting {
|
||||||
tactic: list_tactic,
|
tactic: ListTactic::HorizontalVertical,
|
||||||
separator: ",",
|
separator: ",",
|
||||||
trailing_separator: SeparatorTactic::Never,
|
trailing_separator: SeparatorTactic::Never,
|
||||||
indent: arg_indent,
|
indent: arg_indent,
|
||||||
h_width: one_line_budget,
|
h_width: one_line_budget,
|
||||||
v_width: multi_line_budget,
|
v_width: multi_line_budget,
|
||||||
|
is_expression: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
write_list(&arg_strs, &fmt)
|
write_list(&arg_items, &fmt)
|
||||||
}
|
|
||||||
|
|
||||||
// Gets comments in between items of a list.
|
|
||||||
fn make_comments_for_list<T, I, F1, F2>(&self,
|
|
||||||
prefix: Vec<String>,
|
|
||||||
mut it: I,
|
|
||||||
separator: &str,
|
|
||||||
terminator: &str,
|
|
||||||
get_lo: F1,
|
|
||||||
get_hi: F2,
|
|
||||||
next_span_start: BytePos)
|
|
||||||
-> Vec<String>
|
|
||||||
where I: Iterator<Item=T>,
|
|
||||||
F1: Fn(&T) -> BytePos,
|
|
||||||
F2: Fn(&T) -> BytePos
|
|
||||||
{
|
|
||||||
let mut result = prefix;
|
|
||||||
|
|
||||||
let mut prev_end = get_hi(&it.next().unwrap());
|
|
||||||
for item in it {
|
|
||||||
let cur_start = get_lo(&item);
|
|
||||||
let snippet = self.snippet(codemap::mk_sp(prev_end, cur_start));
|
|
||||||
let mut snippet = snippet.trim();
|
|
||||||
let white_space: &[_] = &[' ', '\t'];
|
|
||||||
if snippet.starts_with(separator) {
|
|
||||||
snippet = snippet[separator.len()..].trim_matches(white_space);
|
|
||||||
} else if snippet.ends_with(separator) {
|
|
||||||
snippet = snippet[..snippet.len()-separator.len()].trim_matches(white_space);
|
|
||||||
}
|
|
||||||
result.push(snippet.to_owned());
|
|
||||||
prev_end = get_hi(&item);
|
|
||||||
}
|
|
||||||
// Get the last commment.
|
|
||||||
// FIXME If you thought the crap with the commas was ugly, just wait.
|
|
||||||
// This is awful. We're going to look from the last item span to the
|
|
||||||
// start of the return type span, then we drop everything after the
|
|
||||||
// first closing paren.
|
|
||||||
// The fix is comments in the AST or a span for the closing paren.
|
|
||||||
let snippet = self.snippet(codemap::mk_sp(prev_end, next_span_start));
|
|
||||||
let snippet = snippet.trim();
|
|
||||||
let snippet = &snippet[..snippet.find_uncommented(terminator).unwrap_or(snippet.len())];
|
|
||||||
let snippet = snippet.trim();
|
|
||||||
result.push(snippet.to_owned());
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_budgets_for_args(&self,
|
fn compute_budgets_for_args(&self,
|
||||||
|
@ -412,12 +379,16 @@ impl<'a> FmtVisitor<'a> {
|
||||||
generics: &ast::Generics,
|
generics: &ast::Generics,
|
||||||
span: Span)
|
span: Span)
|
||||||
{
|
{
|
||||||
let header_str = self.format_header("enum", ident, vis);
|
let header_str = self.format_header("enum ", ident, vis);
|
||||||
self.changes.push_str_span(span, &header_str);
|
self.changes.push_str_span(span, &header_str);
|
||||||
|
|
||||||
let enum_snippet = self.snippet(span);
|
let enum_snippet = self.snippet(span);
|
||||||
let body_start = span.lo + BytePos(enum_snippet.find_uncommented("{").unwrap() as u32 + 1);
|
let body_start = span.lo + BytePos(enum_snippet.find_uncommented("{").unwrap() as u32 + 1);
|
||||||
let generics_str = self.format_generics(generics, body_start);
|
let generics_str = self.format_generics(generics,
|
||||||
|
" {",
|
||||||
|
self.block_indent + self.config.tab_spaces,
|
||||||
|
codemap::mk_sp(span.lo,
|
||||||
|
body_start));
|
||||||
self.changes.push_str_span(span, &generics_str);
|
self.changes.push_str_span(span, &generics_str);
|
||||||
|
|
||||||
self.last_pos = body_start;
|
self.last_pos = body_start;
|
||||||
|
@ -447,75 +418,185 @@ impl<'a> FmtVisitor<'a> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let ast::VariantKind::TupleVariantKind(ref types) = field.node.kind {
|
self.format_missing_with_indent(field.span.lo);
|
||||||
self.format_missing_with_indent(field.span.lo);
|
|
||||||
|
|
||||||
let vis = format_visibility(field.node.vis);
|
match field.node.kind {
|
||||||
self.changes.push_str_span(field.span, vis);
|
ast::VariantKind::TupleVariantKind(ref types) => {
|
||||||
let name = field.node.name.to_string();
|
let vis = format_visibility(field.node.vis);
|
||||||
self.changes.push_str_span(field.span, &name);
|
self.changes.push_str_span(field.span, vis);
|
||||||
|
let name = field.node.name.to_string();
|
||||||
|
self.changes.push_str_span(field.span, &name);
|
||||||
|
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
|
|
||||||
if types.len() > 0 {
|
if types.len() > 0 {
|
||||||
let comments = self.make_comments_for_list(Vec::new(),
|
let items = itemize_list(self.codemap,
|
||||||
types.iter().map(|arg| arg.ty.span),
|
Vec::new(),
|
||||||
",",
|
types.iter(),
|
||||||
")",
|
",",
|
||||||
|span| span.lo,
|
")",
|
||||||
|span| span.hi,
|
|arg| arg.ty.span.lo,
|
||||||
next_span_start);
|
|arg| arg.ty.span.hi,
|
||||||
|
|arg| pprust::ty_to_string(&arg.ty),
|
||||||
|
self.span_after(field.span, "("),
|
||||||
|
next_span_start);
|
||||||
|
|
||||||
let type_strings: Vec<_> = types.iter()
|
result.push('(');
|
||||||
.map(|arg| pprust::ty_to_string(&arg.ty))
|
|
||||||
.zip(comments.into_iter())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
result.push('(');
|
let indent = self.block_indent
|
||||||
|
+ vis.len()
|
||||||
|
+ field.node.name.to_string().len()
|
||||||
|
+ 1; // Open paren
|
||||||
|
|
||||||
let indent = self.block_indent
|
let comma_cost = if self.config.enum_trailing_comma { 1 } else { 0 };
|
||||||
+ vis.len()
|
let budget = self.config.ideal_width - indent - comma_cost - 1; // 1 = )
|
||||||
+ field.node.name.to_string().len()
|
|
||||||
+ 1; // 1 = (
|
|
||||||
|
|
||||||
let comma_cost = if self.config.enum_trailing_comma { 1 } else { 0 };
|
let fmt = ListFormatting {
|
||||||
let budget = self.config.ideal_width - indent - comma_cost - 1; // 1 = )
|
tactic: ListTactic::HorizontalVertical,
|
||||||
|
separator: ",",
|
||||||
|
trailing_separator: SeparatorTactic::Never,
|
||||||
|
indent: indent,
|
||||||
|
h_width: budget,
|
||||||
|
v_width: budget,
|
||||||
|
is_expression: false,
|
||||||
|
};
|
||||||
|
result.push_str(&write_list(&items, &fmt));
|
||||||
|
result.push(')');
|
||||||
|
}
|
||||||
|
|
||||||
let fmt = ListFormatting {
|
if let Some(ref expr) = field.node.disr_expr {
|
||||||
tactic: ListTactic::HorizontalVertical,
|
result.push_str(" = ");
|
||||||
separator: ",",
|
let expr_snippet = self.snippet(expr.span);
|
||||||
trailing_separator: SeparatorTactic::Never,
|
result.push_str(&expr_snippet);
|
||||||
indent: indent,
|
|
||||||
h_width: budget,
|
|
||||||
v_width: budget,
|
|
||||||
};
|
|
||||||
result.push_str(&write_list(&type_strings, &fmt));
|
|
||||||
result.push(')');
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(ref expr) = field.node.disr_expr {
|
// Make sure we do not exceed column limit
|
||||||
result.push_str(" = ");
|
// 4 = " = ,"
|
||||||
let expr_snippet = self.snippet(expr.span);
|
assert!(self.config.max_width >= vis.len() + name.len() + expr_snippet.len() + 4,
|
||||||
result.push_str(&expr_snippet);
|
"Enum variant exceeded column limit");
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure we do not exceed column limit
|
self.changes.push_str_span(field.span, &result);
|
||||||
// 4 = " = ,"
|
|
||||||
assert!(self.config.max_width >= vis.len() + name.len() + expr_snippet.len() + 4,
|
|
||||||
"Enum variant exceeded column limit");
|
|
||||||
}
|
|
||||||
|
|
||||||
self.changes.push_str_span(field.span, &result);
|
if !last_field || self.config.enum_trailing_comma {
|
||||||
|
self.changes.push_str_span(field.span, ",");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ast::VariantKind::StructVariantKind(ref struct_def) => {
|
||||||
|
let result = self.format_struct("",
|
||||||
|
field.node.name,
|
||||||
|
field.node.vis,
|
||||||
|
struct_def,
|
||||||
|
None,
|
||||||
|
field.span,
|
||||||
|
self.block_indent);
|
||||||
|
|
||||||
if !last_field || self.config.enum_trailing_comma {
|
self.changes.push_str_span(field.span, &result)
|
||||||
self.changes.push_str_span(field.span, ",");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: deal with struct-like variants
|
|
||||||
|
|
||||||
self.last_pos = field.span.hi + BytePos(1);
|
self.last_pos = field.span.hi + BytePos(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn format_struct(&self,
|
||||||
|
item_name: &str,
|
||||||
|
ident: ast::Ident,
|
||||||
|
vis: ast::Visibility,
|
||||||
|
struct_def: &ast::StructDef,
|
||||||
|
generics: Option<&ast::Generics>,
|
||||||
|
span: Span,
|
||||||
|
offset: usize) -> String
|
||||||
|
{
|
||||||
|
let mut result = String::with_capacity(1024);
|
||||||
|
|
||||||
|
let header_str = self.format_header(item_name, ident, vis);
|
||||||
|
result.push_str(&header_str);
|
||||||
|
|
||||||
|
if struct_def.fields.len() == 0 {
|
||||||
|
result.push(';');
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_tuple = match struct_def.fields[0].node.kind {
|
||||||
|
ast::StructFieldKind::NamedField(..) => false,
|
||||||
|
ast::StructFieldKind::UnnamedField(..) => true
|
||||||
|
};
|
||||||
|
|
||||||
|
let (opener, terminator) = if is_tuple { ("(", ")") } else { (" {", "}") };
|
||||||
|
|
||||||
|
let generics_str = match generics {
|
||||||
|
Some(g) => self.format_generics(g,
|
||||||
|
opener,
|
||||||
|
offset + header_str.len(),
|
||||||
|
codemap::mk_sp(span.lo,
|
||||||
|
struct_def.fields[0].span.lo)),
|
||||||
|
None => opener.to_owned()
|
||||||
|
};
|
||||||
|
result.push_str(&generics_str);
|
||||||
|
|
||||||
|
let items = itemize_list(self.codemap,
|
||||||
|
Vec::new(),
|
||||||
|
struct_def.fields.iter(),
|
||||||
|
",",
|
||||||
|
terminator,
|
||||||
|
|field| {
|
||||||
|
// Include attributes and doc comments,
|
||||||
|
// if present
|
||||||
|
if field.node.attrs.len() > 0 {
|
||||||
|
field.node.attrs[0].span.lo
|
||||||
|
} else {
|
||||||
|
field.span.lo
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|field| field.node.ty.span.hi,
|
||||||
|
|field| self.format_field(field),
|
||||||
|
self.span_after(span, opener.trim()),
|
||||||
|
span.hi);
|
||||||
|
|
||||||
|
// 2 terminators and a semicolon
|
||||||
|
let used_budget = offset + header_str.len() + generics_str.len() + 3;
|
||||||
|
|
||||||
|
// Conservative approximation
|
||||||
|
let single_line_cost = (span.hi - struct_def.fields[0].span.lo).0;
|
||||||
|
let break_line = !is_tuple ||
|
||||||
|
generics_str.contains('\n') ||
|
||||||
|
single_line_cost as usize + used_budget > self.config.max_width;
|
||||||
|
|
||||||
|
if break_line {
|
||||||
|
let indentation = make_indent(offset + self.config.tab_spaces);
|
||||||
|
result.push('\n');
|
||||||
|
result.push_str(&indentation);
|
||||||
|
}
|
||||||
|
|
||||||
|
let tactic = if break_line { ListTactic::Vertical } else { ListTactic::Horizontal };
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
is_expression: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
result.push_str(&write_list(&items, &fmt));
|
||||||
|
|
||||||
|
if break_line {
|
||||||
|
result.push('\n');
|
||||||
|
result.push_str(&make_indent(offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push_str(terminator);
|
||||||
|
|
||||||
|
if is_tuple {
|
||||||
|
result.push(';');
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
pub fn visit_struct(&mut self,
|
pub fn visit_struct(&mut self,
|
||||||
ident: ast::Ident,
|
ident: ast::Ident,
|
||||||
vis: ast::Visibility,
|
vis: ast::Visibility,
|
||||||
|
@ -523,34 +604,16 @@ impl<'a> FmtVisitor<'a> {
|
||||||
generics: &ast::Generics,
|
generics: &ast::Generics,
|
||||||
span: Span)
|
span: Span)
|
||||||
{
|
{
|
||||||
let header_str = self.format_header("struct", ident, vis);
|
let indent = self.block_indent;
|
||||||
self.changes.push_str_span(span, &header_str);
|
let result = self.format_struct("struct ",
|
||||||
|
ident,
|
||||||
if struct_def.fields.len() == 0 {
|
vis,
|
||||||
assert!(generics.where_clause.predicates.len() == 0,
|
struct_def,
|
||||||
"No-field struct with where clause?");
|
Some(generics),
|
||||||
assert!(generics.lifetimes.len() == 0, "No-field struct with generics?");
|
span,
|
||||||
assert!(generics.ty_params.len() == 0, "No-field struct with generics?");
|
indent);
|
||||||
|
self.changes.push_str_span(span, &result);
|
||||||
self.changes.push_str_span(span, ";");
|
self.last_pos = span.hi;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let generics_str = self.format_generics(generics, struct_def.fields[0].span.lo);
|
|
||||||
self.changes.push_str_span(span, &generics_str);
|
|
||||||
|
|
||||||
let struct_snippet = self.snippet(span);
|
|
||||||
// This will drop the comment in between the header and body.
|
|
||||||
self.last_pos = span.lo + BytePos(struct_snippet.find_uncommented("{").unwrap() as u32 + 1);
|
|
||||||
|
|
||||||
self.block_indent += self.config.tab_spaces;
|
|
||||||
for (i, f) in struct_def.fields.iter().enumerate() {
|
|
||||||
self.visit_field(f, i == struct_def.fields.len() - 1, span.lo, &struct_snippet);
|
|
||||||
}
|
|
||||||
self.block_indent -= self.config.tab_spaces;
|
|
||||||
|
|
||||||
self.format_missing_with_indent(span.lo + BytePos(struct_snippet.rfind('}').unwrap() as u32));
|
|
||||||
self.changes.push_str_span(span, "}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_header(&self,
|
fn format_header(&self,
|
||||||
|
@ -559,42 +622,37 @@ impl<'a> FmtVisitor<'a> {
|
||||||
vis: ast::Visibility)
|
vis: ast::Visibility)
|
||||||
-> String
|
-> String
|
||||||
{
|
{
|
||||||
format!("{}{} {}", format_visibility(vis), item_name, &token::get_ident(ident))
|
format!("{}{}{}", format_visibility(vis), item_name, &token::get_ident(ident))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_generics(&self,
|
fn format_generics(&self,
|
||||||
generics: &ast::Generics,
|
generics: &ast::Generics,
|
||||||
span_end: BytePos)
|
opener: &str,
|
||||||
|
offset: usize,
|
||||||
|
span: Span)
|
||||||
-> String
|
-> String
|
||||||
{
|
{
|
||||||
let mut result = self.rewrite_generics(generics, self.block_indent, span_end);
|
let mut result = self.rewrite_generics(generics, offset, span);
|
||||||
|
|
||||||
if generics.where_clause.predicates.len() > 0 {
|
if generics.where_clause.predicates.len() > 0 || result.contains('\n') {
|
||||||
result.push_str(&self.rewrite_where_clause(&generics.where_clause,
|
result.push_str(&self.rewrite_where_clause(&generics.where_clause,
|
||||||
self.block_indent,
|
self.block_indent,
|
||||||
span_end));
|
span.hi));
|
||||||
result.push_str(&make_indent(self.block_indent));
|
result.push_str(&make_indent(self.block_indent));
|
||||||
result.push_str("\n{");
|
result.push('\n');
|
||||||
|
result.push_str(opener.trim());
|
||||||
} else {
|
} else {
|
||||||
result.push_str(" {");
|
result.push_str(opener);
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Field of a struct
|
// Field of a struct
|
||||||
fn visit_field(&mut self,
|
fn format_field(&self, field: &ast::StructField) -> String {
|
||||||
field: &ast::StructField,
|
if contains_skip(&field.node.attrs) {
|
||||||
last_field: bool,
|
return self.snippet(codemap::mk_sp(field.node.attrs[0].span.lo, field.span.hi));
|
||||||
// These two args are for missing spans hacks.
|
|
||||||
struct_start: BytePos,
|
|
||||||
struct_snippet: &str)
|
|
||||||
{
|
|
||||||
if self.visit_attrs(&field.node.attrs) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
self.format_missing_with_indent(field.span.lo);
|
|
||||||
|
|
||||||
let name = match field.node.kind {
|
let name = match field.node.kind {
|
||||||
ast::StructFieldKind::NamedField(ident, _) => Some(token::get_ident(ident)),
|
ast::StructFieldKind::NamedField(ident, _) => Some(token::get_ident(ident)),
|
||||||
|
@ -606,38 +664,20 @@ impl<'a> FmtVisitor<'a> {
|
||||||
};
|
};
|
||||||
let typ = pprust::ty_to_string(&field.node.ty);
|
let typ = pprust::ty_to_string(&field.node.ty);
|
||||||
|
|
||||||
let mut field_str = match name {
|
let indent = self.block_indent + self.config.tab_spaces;
|
||||||
Some(name) => {
|
let mut attr_str = self.rewrite_attrs(&field.node.attrs, indent);
|
||||||
let budget = self.config.ideal_width - self.block_indent;
|
if attr_str.len() > 0 {
|
||||||
// 3 is being conservative and assuming that there will be a trailing comma.
|
attr_str.push('\n');
|
||||||
if self.block_indent + vis.len() + name.len() + typ.len() + 3 > budget {
|
attr_str.push_str(&make_indent(indent));
|
||||||
format!("{}{}:\n{}{}",
|
|
||||||
vis,
|
|
||||||
name,
|
|
||||||
&make_indent(self.block_indent + self.config.tab_spaces),
|
|
||||||
typ)
|
|
||||||
} else {
|
|
||||||
format!("{}{}: {}", vis, name, typ)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => format!("{}{}", vis, typ),
|
|
||||||
};
|
|
||||||
if !last_field || self.config.struct_trailing_comma {
|
|
||||||
field_str.push(',');
|
|
||||||
}
|
}
|
||||||
self.changes.push_str_span(field.span, &field_str);
|
|
||||||
|
|
||||||
// This hack makes sure we only add comments etc. after the comma, and
|
match name {
|
||||||
// makes sure we don't repeat any commas.
|
Some(name) => format!("{}{}{}: {}", attr_str, vis, name, typ),
|
||||||
let hi = field.span.hi;
|
None => format!("{}{}{}", attr_str, vis, typ)
|
||||||
let comma_pos = match struct_snippet[(hi.0 - struct_start.0) as usize..].find_uncommented(",") {
|
}
|
||||||
Some(i) => i,
|
|
||||||
None => 0,
|
|
||||||
};
|
|
||||||
self.last_pos = hi + BytePos(comma_pos as u32 + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rewrite_generics(&self, generics: &ast::Generics, indent: usize, span_end: BytePos) -> String {
|
fn rewrite_generics(&self, generics: &ast::Generics, offset: usize, span: Span) -> String {
|
||||||
// FIXME convert bounds to where clauses where they get too big or if
|
// FIXME convert bounds to where clauses where they get too big or if
|
||||||
// there is a where clause at all.
|
// there is a where clause at all.
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
|
@ -647,7 +687,7 @@ impl<'a> FmtVisitor<'a> {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
let budget = self.config.max_width - indent - 2;
|
let budget = self.config.max_width - offset - 2;
|
||||||
// TODO might need to insert a newline if the generics are really long
|
// TODO might need to insert a newline if the generics are really long
|
||||||
result.push('<');
|
result.push('<');
|
||||||
|
|
||||||
|
@ -665,30 +705,32 @@ impl<'a> FmtVisitor<'a> {
|
||||||
codemap::mk_sp(l.lifetime.span.lo, hi)
|
codemap::mk_sp(l.lifetime.span.lo, hi)
|
||||||
});
|
});
|
||||||
let ty_spans = tys.iter().map(span_for_ty_param);
|
let ty_spans = tys.iter().map(span_for_ty_param);
|
||||||
let comments = self.make_comments_for_list(Vec::new(),
|
|
||||||
lt_spans.chain(ty_spans),
|
|
||||||
",",
|
|
||||||
">",
|
|
||||||
|sp| sp.lo,
|
|
||||||
|sp| sp.hi,
|
|
||||||
span_end);
|
|
||||||
|
|
||||||
// If there are // comments, keep them multi-line.
|
let mut items = itemize_list(self.codemap,
|
||||||
let mut list_tactic = ListTactic::HorizontalVertical;
|
Vec::new(),
|
||||||
if comments.iter().any(|c| c.contains("//")) {
|
lt_spans.chain(ty_spans),
|
||||||
list_tactic = ListTactic::Vertical;
|
",",
|
||||||
|
">",
|
||||||
|
|sp| sp.lo,
|
||||||
|
|sp| sp.hi,
|
||||||
|
|_| String::new(),
|
||||||
|
self.span_after(span, "<"),
|
||||||
|
span.hi);
|
||||||
|
|
||||||
|
for (item, ty) in items.iter_mut().zip(lt_strs.chain(ty_strs)) {
|
||||||
|
item.item = ty;
|
||||||
}
|
}
|
||||||
|
|
||||||
let generics_strs: Vec<_> = lt_strs.chain(ty_strs).zip(comments.into_iter()).collect();
|
|
||||||
let fmt = ListFormatting {
|
let fmt = ListFormatting {
|
||||||
tactic: list_tactic,
|
tactic: ListTactic::HorizontalVertical,
|
||||||
separator: ",",
|
separator: ",",
|
||||||
trailing_separator: SeparatorTactic::Never,
|
trailing_separator: SeparatorTactic::Never,
|
||||||
indent: indent + 1,
|
indent: offset + 1,
|
||||||
h_width: budget,
|
h_width: budget,
|
||||||
v_width: budget,
|
v_width: budget,
|
||||||
|
is_expression: true,
|
||||||
};
|
};
|
||||||
result.push_str(&write_list(&generics_strs, &fmt));
|
result.push_str(&write_list(&items, &fmt));
|
||||||
|
|
||||||
result.push('>');
|
result.push('>');
|
||||||
|
|
||||||
|
@ -710,18 +752,17 @@ impl<'a> FmtVisitor<'a> {
|
||||||
result.push_str(&make_indent(indent + 4));
|
result.push_str(&make_indent(indent + 4));
|
||||||
result.push_str("where ");
|
result.push_str("where ");
|
||||||
|
|
||||||
let comments = self.make_comments_for_list(Vec::new(),
|
let span_start = span_for_where_pred(&where_clause.predicates[0]).lo;
|
||||||
where_clause.predicates.iter(),
|
let items = itemize_list(self.codemap,
|
||||||
",",
|
Vec::new(),
|
||||||
"{",
|
where_clause.predicates.iter(),
|
||||||
|pred| span_for_where_pred(pred).lo,
|
",",
|
||||||
|pred| span_for_where_pred(pred).hi,
|
"{",
|
||||||
span_end);
|
|pred| span_for_where_pred(pred).lo,
|
||||||
|
|pred| span_for_where_pred(pred).hi,
|
||||||
let where_strs: Vec<_> = where_clause.predicates.iter()
|
|pred| self.rewrite_pred(pred),
|
||||||
.map(|p| (self.rewrite_pred(p)))
|
span_start,
|
||||||
.zip(comments.into_iter())
|
span_end);
|
||||||
.collect();
|
|
||||||
|
|
||||||
let budget = self.config.ideal_width + self.config.leeway - indent - 10;
|
let budget = self.config.ideal_width + self.config.leeway - indent - 10;
|
||||||
let fmt = ListFormatting {
|
let fmt = ListFormatting {
|
||||||
|
@ -731,8 +772,9 @@ impl<'a> FmtVisitor<'a> {
|
||||||
indent: indent + 10,
|
indent: indent + 10,
|
||||||
h_width: budget,
|
h_width: budget,
|
||||||
v_width: budget,
|
v_width: budget,
|
||||||
|
is_expression: true,
|
||||||
};
|
};
|
||||||
result.push_str(&write_list(&where_strs, &fmt));
|
result.push_str(&write_list(&items, &fmt));
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -751,6 +793,12 @@ impl<'a> FmtVisitor<'a> {
|
||||||
pprust::pat_to_string(&arg.pat),
|
pprust::pat_to_string(&arg.pat),
|
||||||
pprust::ty_to_string(&arg.ty))
|
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 {
|
fn span_for_return(ret: &ast::FunctionRetTy) -> Span {
|
||||||
|
|
|
@ -62,6 +62,8 @@ mod expr;
|
||||||
mod imports;
|
mod imports;
|
||||||
mod issues;
|
mod issues;
|
||||||
mod rewrite;
|
mod rewrite;
|
||||||
|
mod string;
|
||||||
|
mod comment;
|
||||||
|
|
||||||
const MIN_STRING: usize = 10;
|
const MIN_STRING: usize = 10;
|
||||||
// When we get scoped annotations, we should have rustfmt::skip.
|
// When we get scoped annotations, we should have rustfmt::skip.
|
||||||
|
|
243
src/lists.rs
243
src/lists.rs
|
@ -8,7 +8,13 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use utils::make_indent;
|
use std::cmp;
|
||||||
|
|
||||||
|
use syntax::codemap::{self, CodeMap, BytePos};
|
||||||
|
|
||||||
|
use utils::{round_up_to_power_of_two, make_indent};
|
||||||
|
use comment::{FindUncommented, rewrite_comment, find_comment_end};
|
||||||
|
use string::before;
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
|
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
|
||||||
pub enum ListTactic {
|
pub enum ListTactic {
|
||||||
|
@ -41,11 +47,38 @@ pub struct ListFormatting<'a> {
|
||||||
pub h_width: usize,
|
pub h_width: usize,
|
||||||
// Available width if we layout vertically
|
// Available width if we layout vertically
|
||||||
pub v_width: usize,
|
pub v_width: usize,
|
||||||
|
// Non-expressions, e.g. items, will have a new line at the end of the list.
|
||||||
|
// Important for comment styles.
|
||||||
|
pub is_expression: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format a list of strings into a string.
|
pub struct ListItem {
|
||||||
// Precondition: all strings in items are trimmed.
|
pub pre_comment: Option<String>,
|
||||||
pub fn write_list<'b>(items: &[(String, String)], formatting: &ListFormatting<'b>) -> String {
|
// Item should include attributes and doc comments
|
||||||
|
pub item: String,
|
||||||
|
pub post_comment: Option<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListItem {
|
||||||
|
pub fn is_multiline(&self) -> bool {
|
||||||
|
self.item.contains('\n') ||
|
||||||
|
self.pre_comment.is_some() ||
|
||||||
|
self.post_comment.as_ref().map(|s| s.contains('\n')).unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_str<S: Into<String>>(s: S) -> ListItem {
|
||||||
|
ListItem {
|
||||||
|
pre_comment: None,
|
||||||
|
item: s.into(),
|
||||||
|
post_comment: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format a list of commented items into a string.
|
||||||
|
// FIXME: this has grown into a monstrosity
|
||||||
|
// TODO: add unit tests
|
||||||
|
pub fn write_list<'b>(items: &[ListItem], formatting: &ListFormatting<'b>) -> String {
|
||||||
if items.len() == 0 {
|
if items.len() == 0 {
|
||||||
return String::new();
|
return String::new();
|
||||||
}
|
}
|
||||||
|
@ -68,7 +101,7 @@ pub fn write_list<'b>(items: &[(String, String)], formatting: &ListFormatting<'b
|
||||||
debug!("write_list: total_width: {}, total_sep_len: {}, h_width: {}",
|
debug!("write_list: total_width: {}, total_sep_len: {}, h_width: {}",
|
||||||
total_width, total_sep_len, formatting.h_width);
|
total_width, total_sep_len, formatting.h_width);
|
||||||
tactic = if fits_single &&
|
tactic = if fits_single &&
|
||||||
!items.iter().any(|&(ref s, _)| s.contains('\n')) {
|
!items.iter().any(ListItem::is_multiline) {
|
||||||
ListTactic::Horizontal
|
ListTactic::Horizontal
|
||||||
} else {
|
} else {
|
||||||
ListTactic::Vertical
|
ListTactic::Vertical
|
||||||
|
@ -81,6 +114,11 @@ pub fn write_list<'b>(items: &[(String, String)], formatting: &ListFormatting<'b
|
||||||
tactic = ListTactic::Horizontal;
|
tactic = ListTactic::Horizontal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Switch to vertical mode if we find non-block comments.
|
||||||
|
if items.iter().any(has_line_pre_comment) {
|
||||||
|
tactic = ListTactic::Vertical;
|
||||||
|
}
|
||||||
|
|
||||||
// Now that we know how we will layout, we can decide for sure if there
|
// Now that we know how we will layout, we can decide for sure if there
|
||||||
// will be a trailing separator.
|
// will be a trailing separator.
|
||||||
let trailing_separator = needs_trailing_separator(formatting.trailing_separator, tactic);
|
let trailing_separator = needs_trailing_separator(formatting.trailing_separator, tactic);
|
||||||
|
@ -92,13 +130,16 @@ pub fn write_list<'b>(items: &[(String, String)], formatting: &ListFormatting<'b
|
||||||
} else {
|
} else {
|
||||||
total_width + items.len() * (formatting.indent + 1)
|
total_width + items.len() * (formatting.indent + 1)
|
||||||
};
|
};
|
||||||
let mut result = String::with_capacity(alloc_width);
|
let mut result = String::with_capacity(round_up_to_power_of_two(alloc_width));
|
||||||
|
|
||||||
let mut line_len = 0;
|
let mut line_len = 0;
|
||||||
let indent_str = &make_indent(formatting.indent);
|
let indent_str = &make_indent(formatting.indent);
|
||||||
for (i, &(ref item, ref comment)) in items.iter().enumerate() {
|
for (i, item) in items.iter().enumerate() {
|
||||||
let first = i == 0;
|
let first = i == 0;
|
||||||
let separate = i != items.len() - 1 || trailing_separator;
|
let last = i == items.len() - 1;
|
||||||
|
let separate = !last || trailing_separator;
|
||||||
|
let item_sep_len = if separate { sep_len } else { 0 };
|
||||||
|
let item_width = item.item.len() + item_sep_len;
|
||||||
|
|
||||||
match tactic {
|
match tactic {
|
||||||
ListTactic::Horizontal if !first => {
|
ListTactic::Horizontal if !first => {
|
||||||
|
@ -109,12 +150,9 @@ pub fn write_list<'b>(items: &[(String, String)], formatting: &ListFormatting<'b
|
||||||
result.push_str(indent_str);
|
result.push_str(indent_str);
|
||||||
}
|
}
|
||||||
ListTactic::Mixed => {
|
ListTactic::Mixed => {
|
||||||
let mut item_width = item.len();
|
let total_width = total_item_width(item) + item_sep_len;
|
||||||
if separate {
|
|
||||||
item_width += sep_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
if line_len > 0 && line_len + item_width > formatting.v_width {
|
if line_len > 0 && line_len + total_width > formatting.v_width {
|
||||||
result.push('\n');
|
result.push('\n');
|
||||||
result.push_str(indent_str);
|
result.push_str(indent_str);
|
||||||
line_len = 0;
|
line_len = 0;
|
||||||
|
@ -125,35 +163,161 @@ pub fn write_list<'b>(items: &[(String, String)], formatting: &ListFormatting<'b
|
||||||
line_len += 1;
|
line_len += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
line_len += item_width;
|
line_len += total_width;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
result.push_str(item);
|
// Pre-comments
|
||||||
|
if let Some(ref comment) = item.pre_comment {
|
||||||
|
result.push_str(&rewrite_comment(comment,
|
||||||
|
// Block style in non-vertical mode
|
||||||
|
tactic != ListTactic::Vertical,
|
||||||
|
1000,
|
||||||
|
formatting.indent));
|
||||||
|
|
||||||
if tactic != ListTactic::Vertical && comment.len() > 0 {
|
if tactic == ListTactic::Vertical {
|
||||||
if !comment.starts_with('\n') {
|
result.push('\n');
|
||||||
|
result.push_str(indent_str);
|
||||||
|
} else {
|
||||||
result.push(' ');
|
result.push(' ');
|
||||||
}
|
}
|
||||||
result.push_str(comment);
|
}
|
||||||
|
|
||||||
|
result.push_str(&item.item);
|
||||||
|
|
||||||
|
// Post-comments
|
||||||
|
if tactic != ListTactic::Vertical && item.post_comment.is_some() {
|
||||||
|
// We'll assume it'll fit on one line at this point
|
||||||
|
let formatted_comment = rewrite_comment(item.post_comment.as_ref().unwrap(),
|
||||||
|
true,
|
||||||
|
1000,
|
||||||
|
0);
|
||||||
|
|
||||||
|
result.push(' ');
|
||||||
|
result.push_str(&formatted_comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
if separate {
|
if separate {
|
||||||
result.push_str(formatting.separator);
|
result.push_str(formatting.separator);
|
||||||
}
|
}
|
||||||
|
|
||||||
if tactic == ListTactic::Vertical && comment.len() > 0 {
|
if tactic == ListTactic::Vertical && item.post_comment.is_some() {
|
||||||
if !comment.starts_with('\n') {
|
let width = formatting.v_width - item_width - 1; // Space between item and comment
|
||||||
result.push(' ');
|
let offset = formatting.indent + item_width + 1;
|
||||||
}
|
let comment = item.post_comment.as_ref().unwrap();
|
||||||
result.push_str(comment);
|
// 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;
|
||||||
|
|
||||||
|
let formatted_comment = rewrite_comment(comment,
|
||||||
|
block_style,
|
||||||
|
width,
|
||||||
|
offset);
|
||||||
|
|
||||||
|
result.push(' ');
|
||||||
|
result.push_str(&formatted_comment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn has_line_pre_comment(item: &ListItem) -> bool {
|
||||||
|
match item.pre_comment {
|
||||||
|
Some(ref comment) => comment.starts_with("//"),
|
||||||
|
None => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turns a list into a vector of items with associated comments.
|
||||||
|
// TODO: we probably do not want to take a terminator any more. Instead, we
|
||||||
|
// should demand a proper span end.
|
||||||
|
pub fn itemize_list<T, I, F1, F2, F3>(codemap: &CodeMap,
|
||||||
|
prefix: Vec<ListItem>,
|
||||||
|
it: I,
|
||||||
|
separator: &str,
|
||||||
|
terminator: &str,
|
||||||
|
get_lo: F1,
|
||||||
|
get_hi: F2,
|
||||||
|
get_item: F3,
|
||||||
|
mut prev_span_end: BytePos,
|
||||||
|
next_span_start: BytePos)
|
||||||
|
-> Vec<ListItem>
|
||||||
|
where I: Iterator<Item=T>,
|
||||||
|
F1: Fn(&T) -> BytePos,
|
||||||
|
F2: Fn(&T) -> BytePos,
|
||||||
|
F3: Fn(&T) -> String
|
||||||
|
{
|
||||||
|
let mut result = prefix;
|
||||||
|
let mut new_it = it.peekable();
|
||||||
|
let white_space: &[_] = &[' ', '\t'];
|
||||||
|
|
||||||
|
while let Some(item) = new_it.next() {
|
||||||
|
// Pre-comment
|
||||||
|
let pre_snippet = codemap.span_to_snippet(codemap::mk_sp(prev_span_end,
|
||||||
|
get_lo(&item)))
|
||||||
|
.unwrap();
|
||||||
|
let pre_snippet = pre_snippet.trim();
|
||||||
|
let pre_comment = if pre_snippet.len() > 0 {
|
||||||
|
Some(pre_snippet.to_owned())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
// Post-comment
|
||||||
|
let next_start = match new_it.peek() {
|
||||||
|
Some(ref next_item) => get_lo(next_item),
|
||||||
|
None => next_span_start
|
||||||
|
};
|
||||||
|
let post_snippet = codemap.span_to_snippet(codemap::mk_sp(get_hi(&item),
|
||||||
|
next_start))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let comment_end = match new_it.peek() {
|
||||||
|
Some(..) => {
|
||||||
|
if let Some(start) = before(&post_snippet, "/*", "\n") {
|
||||||
|
// Block-style post-comment. Either before or after the separator.
|
||||||
|
cmp::max(find_comment_end(&post_snippet[start..]).unwrap() + start,
|
||||||
|
post_snippet.find_uncommented(separator).unwrap() + separator.len())
|
||||||
|
} else if let Some(idx) = post_snippet.find('\n') {
|
||||||
|
idx + 1
|
||||||
|
} else {
|
||||||
|
post_snippet.len()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
post_snippet.find_uncommented(terminator)
|
||||||
|
.unwrap_or(post_snippet.len())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
prev_span_end = get_hi(&item) + BytePos(comment_end as u32);
|
||||||
|
let mut post_snippet = post_snippet[..comment_end].trim();
|
||||||
|
|
||||||
|
if post_snippet.starts_with(separator) {
|
||||||
|
post_snippet = post_snippet[separator.len()..]
|
||||||
|
.trim_matches(white_space);
|
||||||
|
} else if post_snippet.ends_with(separator) {
|
||||||
|
post_snippet = post_snippet[..post_snippet.len()-separator.len()]
|
||||||
|
.trim_matches(white_space);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push(ListItem {
|
||||||
|
pre_comment: pre_comment,
|
||||||
|
item: get_item(&item),
|
||||||
|
post_comment: if post_snippet.len() > 0 {
|
||||||
|
Some(post_snippet.to_owned())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
fn needs_trailing_separator(separator_tactic: SeparatorTactic, list_tactic: ListTactic) -> bool {
|
fn needs_trailing_separator(separator_tactic: SeparatorTactic, list_tactic: ListTactic) -> bool {
|
||||||
match separator_tactic {
|
match separator_tactic {
|
||||||
SeparatorTactic::Always => true,
|
SeparatorTactic::Always => true,
|
||||||
|
@ -162,16 +326,25 @@ fn needs_trailing_separator(separator_tactic: SeparatorTactic, list_tactic: List
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_width(items:&[(String, String)]) -> usize {
|
fn calculate_width(items: &[ListItem]) -> usize {
|
||||||
let missed_width = items.iter().map(|&(_, ref s)| {
|
items.iter().map(total_item_width).fold(0, |a, l| a + l)
|
||||||
let text_len = s.trim().len();
|
}
|
||||||
if text_len > 0 {
|
|
||||||
// We'll put a space before any comment.
|
fn total_item_width(item: &ListItem) -> usize {
|
||||||
text_len + 1
|
comment_len(&item.pre_comment) + comment_len(&item.post_comment) + item.item.len()
|
||||||
} else {
|
}
|
||||||
text_len
|
|
||||||
}
|
fn comment_len(comment: &Option<String>) -> usize {
|
||||||
}).fold(0, |a, l| a + l);
|
match comment {
|
||||||
let item_width = items.iter().map(|&(ref s, _)| s.len()).fold(0, |a, l| a + l);
|
&Some(ref s) => {
|
||||||
missed_width + item_width
|
let text_len = s.trim().len();
|
||||||
|
if text_len > 0 {
|
||||||
|
// We'll put " /*" before and " */" after inline comments.
|
||||||
|
text_len + 6
|
||||||
|
} else {
|
||||||
|
text_len
|
||||||
|
}
|
||||||
|
},
|
||||||
|
&None => 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
103
src/string.rs
Normal file
103
src/string.rs
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// Format string literals.
|
||||||
|
|
||||||
|
use utils::{make_indent, next_char, prev_char, round_up_to_power_of_two};
|
||||||
|
|
||||||
|
use MIN_STRING;
|
||||||
|
|
||||||
|
pub struct StringFormat<'a> {
|
||||||
|
pub opener: &'a str,
|
||||||
|
pub closer: &'a str,
|
||||||
|
pub line_start: &'a str,
|
||||||
|
pub line_end: &'a str,
|
||||||
|
pub width: usize,
|
||||||
|
pub offset: usize,
|
||||||
|
pub trim_end: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: simplify this!
|
||||||
|
pub fn rewrite_string<'a>(s: &str, fmt: &StringFormat<'a>) -> String {
|
||||||
|
// FIXME I bet this stomps unicode escapes in the source string
|
||||||
|
// TODO if lo.col > IDEAL - 10, start a new line (need cur indent for that)
|
||||||
|
|
||||||
|
let indent = make_indent(fmt.offset);
|
||||||
|
let indent = &indent;
|
||||||
|
|
||||||
|
let mut cur_start = 0;
|
||||||
|
let mut result = String::with_capacity(round_up_to_power_of_two(s.len()));
|
||||||
|
result.push_str(fmt.opener);
|
||||||
|
|
||||||
|
let ender_length = fmt.line_end.len();
|
||||||
|
let max_chars = fmt.width.checked_sub(fmt.opener.len()).unwrap_or(0)
|
||||||
|
.checked_sub(ender_length).unwrap_or(1);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut cur_end = cur_start + max_chars;
|
||||||
|
|
||||||
|
if cur_end >= s.len() {
|
||||||
|
result.push_str(&s[cur_start..]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we're on a char boundary.
|
||||||
|
cur_end = next_char(&s, cur_end);
|
||||||
|
|
||||||
|
// Push cur_end left until we reach whitespace.
|
||||||
|
while !s.char_at(cur_end - 1).is_whitespace() {
|
||||||
|
cur_end = prev_char(&s, cur_end);
|
||||||
|
|
||||||
|
if cur_end - cur_start < MIN_STRING {
|
||||||
|
// We can't break at whitespace, fall back to splitting
|
||||||
|
// anywhere that doesn't break an escape sequence.
|
||||||
|
cur_end = next_char(&s, cur_start + max_chars);
|
||||||
|
while s.char_at(prev_char(&s, cur_end)) == '\\' {
|
||||||
|
cur_end = prev_char(&s, cur_end);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Make sure there is no whitespace to the right of the break.
|
||||||
|
while cur_end < s.len() && s.char_at(cur_end).is_whitespace() {
|
||||||
|
cur_end = next_char(&s, cur_end + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let line: &str = if fmt.trim_end {
|
||||||
|
&s[cur_start..cur_end].trim_right_matches(char::is_whitespace)
|
||||||
|
} else {
|
||||||
|
&s[cur_start..cur_end]
|
||||||
|
};
|
||||||
|
|
||||||
|
result.push_str(line);
|
||||||
|
result.push_str(fmt.line_end);
|
||||||
|
result.push('\n');
|
||||||
|
result.push_str(indent);
|
||||||
|
result.push_str(fmt.line_start);
|
||||||
|
|
||||||
|
cur_start = cur_end;
|
||||||
|
}
|
||||||
|
result.push_str(fmt.closer);
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
// Checks if a appears before b in given string and, if so, returns the index of
|
||||||
|
// a.
|
||||||
|
// FIXME: could be more generic
|
||||||
|
pub fn before<'x>(s: &'x str, a: &str, b: &str) -> Option<usize> {
|
||||||
|
s.find(a).and_then(|i| {
|
||||||
|
match s.find(b) {
|
||||||
|
Some(j) if j <= i => None,
|
||||||
|
_ => Some(i)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
80
src/utils.rs
80
src/utils.rs
|
@ -8,73 +8,9 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use syntax::ast::Visibility;
|
use syntax::ast::{Visibility, Attribute, MetaItem, MetaItem_};
|
||||||
|
|
||||||
pub trait FindUncommented {
|
use SKIP_ANNOTATION;
|
||||||
fn find_uncommented(&self, pat: &str) -> Option<usize>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FindUncommented for str {
|
|
||||||
fn find_uncommented(&self, pat: &str) -> Option<usize> {
|
|
||||||
let mut needle_iter = pat.chars();
|
|
||||||
let mut possible_comment = false;
|
|
||||||
|
|
||||||
for (i, b) in self.char_indices() {
|
|
||||||
match needle_iter.next() {
|
|
||||||
Some(c) => {
|
|
||||||
if b != c {
|
|
||||||
needle_iter = pat.chars();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => return Some(i - pat.len())
|
|
||||||
}
|
|
||||||
|
|
||||||
if possible_comment {
|
|
||||||
if b == '/' {
|
|
||||||
return self[(i+1)..].find('\n')
|
|
||||||
.and_then(|end| {
|
|
||||||
self[(end + i + 2)..].find_uncommented(pat)
|
|
||||||
.map(|idx| idx + end + i + 2)
|
|
||||||
});
|
|
||||||
} else if b == '*' {
|
|
||||||
return self[(i+1)..].find("*/")
|
|
||||||
.and_then(|end| {
|
|
||||||
self[(end + i + 3)..].find_uncommented(pat)
|
|
||||||
.map(|idx| idx + end + i + 3)
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
possible_comment = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
possible_comment = b == '/';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle case where the pattern is a suffix of the search string
|
|
||||||
match needle_iter.next() {
|
|
||||||
Some(_) => None,
|
|
||||||
None => Some(self.len() - pat.len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_find_uncommented() {
|
|
||||||
fn check(haystack: &str, needle: &str, expected: Option<usize>) {
|
|
||||||
assert_eq!(expected, haystack.find_uncommented(needle));
|
|
||||||
}
|
|
||||||
|
|
||||||
check("/*//*/test", "test", Some(6));
|
|
||||||
check("//test\ntest", "test", Some(7));
|
|
||||||
check("/* comment only */", "whatever", None);
|
|
||||||
check("/* comment */ some text /* more commentary */ result", "result", Some(46));
|
|
||||||
check("sup // sup", "p", Some(2));
|
|
||||||
check("sup", "x", None);
|
|
||||||
check("π? /**/ π is nice!", "π is nice", Some(9));
|
|
||||||
check("/*sup yo? \n sup*/ sup", "p", Some(20));
|
|
||||||
check("hel/*lohello*/lo", "hello", None);
|
|
||||||
check("acb", "ab", None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prev_char(s: &str, mut i: usize) -> usize {
|
pub fn prev_char(s: &str, mut i: usize) -> usize {
|
||||||
|
@ -114,6 +50,18 @@ pub fn format_visibility(vis: Visibility) -> &'static str {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_skip(meta_item: &MetaItem) -> bool {
|
||||||
|
match meta_item.node {
|
||||||
|
MetaItem_::MetaWord(ref s) => *s == SKIP_ANNOTATION,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn contains_skip(attrs: &[Attribute]) -> bool {
|
||||||
|
attrs.iter().any(|a| is_skip(&a.node.value))
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[cfg(target_pointer_width="64")]
|
#[cfg(target_pointer_width="64")]
|
||||||
// Based on the trick layed out at
|
// Based on the trick layed out at
|
||||||
|
|
|
@ -15,7 +15,6 @@ use syntax::visit;
|
||||||
use utils;
|
use utils;
|
||||||
use config::Config;
|
use config::Config;
|
||||||
|
|
||||||
use SKIP_ANNOTATION;
|
|
||||||
use changes::ChangeSet;
|
use changes::ChangeSet;
|
||||||
use rewrite::{Rewrite, RewriteContext};
|
use rewrite::{Rewrite, RewriteContext};
|
||||||
|
|
||||||
|
@ -120,7 +119,7 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> {
|
||||||
constness,
|
constness,
|
||||||
abi,
|
abi,
|
||||||
vis,
|
vis,
|
||||||
b.span.lo);
|
codemap::mk_sp(s.lo, b.span.lo));
|
||||||
self.changes.push_str_span(s, &new_fn);
|
self.changes.push_str_span(s, &new_fn);
|
||||||
}
|
}
|
||||||
visit::FkMethod(ident, ref sig, vis) => {
|
visit::FkMethod(ident, ref sig, vis) => {
|
||||||
|
@ -133,7 +132,7 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> {
|
||||||
&sig.constness,
|
&sig.constness,
|
||||||
&sig.abi,
|
&sig.abi,
|
||||||
vis.unwrap_or(ast::Visibility::Inherited),
|
vis.unwrap_or(ast::Visibility::Inherited),
|
||||||
b.span.lo);
|
codemap::mk_sp(s.lo, b.span.lo));
|
||||||
self.changes.push_str_span(s, &new_fn);
|
self.changes.push_str_span(s, &new_fn);
|
||||||
}
|
}
|
||||||
visit::FkFnBlock(..) => {}
|
visit::FkFnBlock(..) => {}
|
||||||
|
@ -305,26 +304,22 @@ impl<'a> FmtVisitor<'a> {
|
||||||
let first = &attrs[0];
|
let first = &attrs[0];
|
||||||
self.format_missing_with_indent(first.span.lo);
|
self.format_missing_with_indent(first.span.lo);
|
||||||
|
|
||||||
match self.rewrite_attrs(attrs, self.block_indent) {
|
if utils::contains_skip(attrs) {
|
||||||
Some(s) => {
|
true
|
||||||
self.changes.push_str_span(first.span, &s);
|
} else {
|
||||||
let last = attrs.last().unwrap();
|
let rewrite = self.rewrite_attrs(attrs, self.block_indent);
|
||||||
self.last_pos = last.span.hi;
|
self.changes.push_str_span(first.span, &rewrite);
|
||||||
false
|
let last = attrs.last().unwrap();
|
||||||
}
|
self.last_pos = last.span.hi;
|
||||||
None => true
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rewrite_attrs(&self, attrs: &[ast::Attribute], indent: usize) -> Option<String> {
|
pub fn rewrite_attrs(&self, attrs: &[ast::Attribute], indent: usize) -> String {
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
let indent = utils::make_indent(indent);
|
let indent = utils::make_indent(indent);
|
||||||
|
|
||||||
for (i, a) in attrs.iter().enumerate() {
|
for (i, a) in attrs.iter().enumerate() {
|
||||||
if is_skip(&a.node.value) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let a_str = self.snippet(a.span);
|
let a_str = self.snippet(a.span);
|
||||||
|
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
|
@ -351,13 +346,6 @@ impl<'a> FmtVisitor<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(result)
|
result
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_skip(meta_item: &ast::MetaItem) -> bool {
|
|
||||||
match meta_item.node {
|
|
||||||
ast::MetaItem_::MetaWord(ref s) => *s == SKIP_ANNOTATION,
|
|
||||||
_ => false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ newline_style = "Unix"
|
||||||
fn_brace_style = "SameLineWhere"
|
fn_brace_style = "SameLineWhere"
|
||||||
fn_return_indent = "WithArgs"
|
fn_return_indent = "WithArgs"
|
||||||
fn_args_paren_newline = true
|
fn_args_paren_newline = true
|
||||||
struct_trailing_comma = true
|
struct_trailing_comma = "Vertical"
|
||||||
struct_lit_trailing_comma = "Vertical"
|
struct_lit_trailing_comma = "Vertical"
|
||||||
enum_trailing_comma = true
|
enum_trailing_comma = true
|
||||||
report_todo = "Always"
|
report_todo = "Always"
|
||||||
|
|
11
tests/source/fn-simple.rs
Normal file
11
tests/source/fn-simple.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
fn simple(/*pre-comment on a function!?*/ i: i32/*yes, it's possible! */
|
||||||
|
,response: NoWay /* hose */) {"cool"}
|
||||||
|
|
||||||
|
|
||||||
|
fn weird_comment(/* /*/ double level */ comment */ x: Hello /*/*/* tripple, even */*/*/,
|
||||||
|
// Does this work?
|
||||||
|
y: World
|
||||||
|
) {
|
||||||
|
simple(/* does this preserve comments now? */ 42, NoWay)
|
||||||
|
}
|
|
@ -43,7 +43,7 @@ fn qux(a: dadsfa, // Comment 1
|
||||||
|
|
||||||
/// Blah blah blah.
|
/// Blah blah blah.
|
||||||
impl Bar {
|
impl Bar {
|
||||||
fn foo(&mut self, a: sdfsdfcccccccccccccccccccccccccccccccccccccccccccccccccccccccc, // comment on a
|
fn foo(&mut self, a: sdfsdfcccccccccccccccccccccccccccccccccccccccccccccccccc, // comment on a
|
||||||
b: sdfasdfsdfasfs /*closing comment*/ ) -> isize {}
|
b: sdfasdfsdfasfs /*closing comment*/ ) -> isize {}
|
||||||
|
|
||||||
/// Blah blah blah.
|
/// Blah blah blah.
|
||||||
|
|
70
tests/source/structs.rs
Normal file
70
tests/source/structs.rs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
|
||||||
|
/// A Doc comment
|
||||||
|
#[AnAttribute]
|
||||||
|
pub struct Foo {
|
||||||
|
#[rustfmt_skip]
|
||||||
|
f : SomeType, // Comment beside a field
|
||||||
|
f: SomeType, // Comment beside a field
|
||||||
|
// Comment on a field
|
||||||
|
#[AnAttribute]
|
||||||
|
g: SomeOtherType,
|
||||||
|
/// A doc comment on a field
|
||||||
|
h: AThirdType,
|
||||||
|
pub i: TypeForPublicField
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Bar;
|
||||||
|
|
||||||
|
struct NewType(Type, OtherType);
|
||||||
|
|
||||||
|
struct
|
||||||
|
NewInt <T: Copy>(pub i32, SomeType /* inline comment */, T /* sup */
|
||||||
|
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
struct Qux<'a,
|
||||||
|
N: Clone + 'a,
|
||||||
|
E: Clone + 'a,
|
||||||
|
G: Labeller<'a, N, E> + GraphWalk<'a, N, E>,
|
||||||
|
W: Write + Copy>
|
||||||
|
(
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, // Comment
|
||||||
|
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB,
|
||||||
|
#[AnAttr]
|
||||||
|
// Comment
|
||||||
|
/// Testdoc
|
||||||
|
G,
|
||||||
|
pub W,
|
||||||
|
);
|
||||||
|
|
||||||
|
struct Tuple(/*Comment 1*/ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,
|
||||||
|
/* Comment 2 */ BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB,);
|
||||||
|
|
||||||
|
// With a where clause and generics.
|
||||||
|
pub struct Foo<'a, Y: Baz>
|
||||||
|
where X: Whatever
|
||||||
|
{
|
||||||
|
f: SomeType, // Comment beside a field
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Baz {
|
||||||
|
a: A, // Comment A
|
||||||
|
b: B, // Comment B
|
||||||
|
c: C, // Comment C
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Baz
|
||||||
|
{
|
||||||
|
// Comment A
|
||||||
|
a: A,
|
||||||
|
// Comment B
|
||||||
|
b: B,
|
||||||
|
// Comment C
|
||||||
|
c: C,}
|
||||||
|
|
||||||
|
// Will this be a one-liner?
|
||||||
|
struct Tuple(
|
||||||
|
A, //Comment
|
||||||
|
B
|
||||||
|
);
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
// Comment on foo.
|
// Comment on foo.
|
||||||
fn foo<F, G>(a: aaaaaaaaaaaaa, // A comment
|
fn foo<F, G>(a: aaaaaaaaaaaaa, // A comment
|
||||||
b: bbbbbbbbbbbbb, /* a second comment */
|
b: bbbbbbbbbbbbb, // a second comment
|
||||||
c: ccccccccccccc,
|
c: ccccccccccccc,
|
||||||
// Newline comment
|
// Newline comment
|
||||||
d: ddddddddddddd,
|
d: ddddddddddddd,
|
||||||
// A multi line comment
|
// A multi line comment
|
||||||
// between args.
|
// between args.
|
||||||
e: eeeeeeeeeeeee /* comment before paren*/)
|
e: eeeeeeeeeeeee /* comment before paren */)
|
||||||
-> bar
|
-> bar
|
||||||
where F: Foo, // COmment after where clause
|
where F: Foo, // COmment after where clause
|
||||||
G: Goo /* final comment */
|
G: Goo /* final comment */
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#[atrr]
|
#[atrr]
|
||||||
pub enum Test {
|
pub enum Test {
|
||||||
A,
|
A,
|
||||||
B(u32, A /* comment */),
|
B(u32, A /* comment */, SomeType),
|
||||||
/// Doc comment
|
/// Doc comment
|
||||||
C,
|
C,
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,18 @@ enum Bar {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum LongVariants {
|
enum LongVariants {
|
||||||
First(LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG, // small comment
|
First(LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG, // comment
|
||||||
VARIANT),
|
VARIANT),
|
||||||
// This is the second variant
|
// This is the second variant
|
||||||
Second,
|
Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum StructLikeVariants {
|
||||||
|
Normal(u32, String),
|
||||||
|
StructLike {
|
||||||
|
x: i32, // Test comment
|
||||||
|
// Pre-comment
|
||||||
|
#[Attr50]
|
||||||
|
y: SomeType, // Aanother Comment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
16
tests/target/fn-simple.rs
Normal file
16
tests/target/fn-simple.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
|
||||||
|
fn simple(// pre-comment on a function!?
|
||||||
|
i: i32, // yes, it's possible!
|
||||||
|
response: NoWay /* hose */) {
|
||||||
|
"cool"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn weird_comment(// /*/ double level */ comment
|
||||||
|
x: Hello, // /*/* tripple, even */*/
|
||||||
|
// Does this work?
|
||||||
|
y: World) {
|
||||||
|
simple(// does this preserve comments now?
|
||||||
|
42,
|
||||||
|
NoWay)
|
||||||
|
}
|
|
@ -36,10 +36,10 @@ fn foo()
|
||||||
hello!()
|
hello!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn baz<'a: 'b, /* comment on 'a */
|
fn baz<'a: 'b, // comment on 'a
|
||||||
T: SomsssssssssssssssssssssssssssssssssssssssssssssssssssssseType /* comment on T */>
|
T: SomsssssssssssssssssssssssssssssssssssssssssssssssssssssseType /* comment on T */>
|
||||||
(a: A,
|
(a: A,
|
||||||
b: B, /* comment on b */
|
b: B, // comment on b
|
||||||
c: C)
|
c: C)
|
||||||
-> Bob {
|
-> Bob {
|
||||||
#[attr1]
|
#[attr1]
|
||||||
|
@ -65,8 +65,9 @@ fn qux(a: dadsfa, // Comment 1
|
||||||
/// Blah blah blah.
|
/// Blah blah blah.
|
||||||
impl Bar {
|
impl Bar {
|
||||||
fn foo(&mut self,
|
fn foo(&mut self,
|
||||||
a: sdfsdfcccccccccccccccccccccccccccccccccccccccccccccccccccccccc, // comment on a
|
a: sdfsdfcccccccccccccccccccccccccccccccccccccccccccccccccc, /* commen
|
||||||
b: sdfasdfsdfasfs /*closing comment*/)
|
* t on a */
|
||||||
|
b: sdfasdfsdfasfs /* closing comment */)
|
||||||
-> isize {
|
-> isize {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,32 @@ pub struct Foo {
|
||||||
|
|
||||||
struct Bar;
|
struct Bar;
|
||||||
|
|
||||||
|
struct NewType(Type, OtherType);
|
||||||
|
|
||||||
|
struct NewInt<T: Copy>(pub i32, SomeType /* inline comment */, T /* sup */);
|
||||||
|
|
||||||
|
struct Qux<'a,
|
||||||
|
N: Clone + 'a,
|
||||||
|
E: Clone + 'a,
|
||||||
|
G: Labeller<'a, N, E> + GraphWalk<'a, N, E>,
|
||||||
|
W: Write + Copy>
|
||||||
|
(
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, // Comment
|
||||||
|
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB,
|
||||||
|
#[AnAttr]
|
||||||
|
// Comment
|
||||||
|
/// Testdoc
|
||||||
|
G,
|
||||||
|
pub W,
|
||||||
|
);
|
||||||
|
|
||||||
|
struct Tuple(
|
||||||
|
// Comment 1
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,
|
||||||
|
// Comment 2
|
||||||
|
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB,
|
||||||
|
);
|
||||||
|
|
||||||
// With a where clause and generics.
|
// With a where clause and generics.
|
||||||
pub struct Foo<'a, Y: Baz>
|
pub struct Foo<'a, Y: Baz>
|
||||||
where X: Whatever
|
where X: Whatever
|
||||||
|
@ -36,3 +62,6 @@ struct Baz {
|
||||||
// Comment C
|
// Comment C
|
||||||
c: C,
|
c: C,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Will this be a one-liner?
|
||||||
|
struct Tuple(A /* Comment */, B);
|
||||||
|
|
|
@ -8,4 +8,8 @@ fn foo() {
|
||||||
aaaaaaaaaaaaaaaaaaaaaaaaa,
|
aaaaaaaaaaaaaaaaaaaaaaaaa,
|
||||||
aaaa);
|
aaaa);
|
||||||
let a = (a,);
|
let a = (a,);
|
||||||
|
|
||||||
|
let b = (// This is a comment
|
||||||
|
b, // Comment
|
||||||
|
b /* Trailing comment */);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue