Merge pull request #93 from marcusklaas/tuple-structs
Format tuple like structs
This commit is contained in:
commit
8cef6785bf
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_return_indent: ReturnIndent,
|
||||
pub fn_args_paren_newline: bool,
|
||||
pub struct_trailing_comma: bool,
|
||||
pub struct_trailing_comma: SeparatorTactic,
|
||||
pub struct_lit_trailing_comma: SeparatorTactic,
|
||||
pub enum_trailing_comma: bool,
|
||||
pub report_todo: ReportTactic,
|
||||
|
@ -6,7 +6,7 @@ newline_style = "Unix"
|
||||
fn_brace_style = "SameLineWhere"
|
||||
fn_return_indent = "WithArgs"
|
||||
fn_args_paren_newline = true
|
||||
struct_trailing_comma = true
|
||||
struct_trailing_comma = "Vertical"
|
||||
struct_lit_trailing_comma = "Vertical"
|
||||
enum_trailing_comma = true
|
||||
report_todo = "Always"
|
||||
|
308
src/expr.rs
308
src/expr.rs
@ -8,17 +8,16 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use utils::*;
|
||||
use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};
|
||||
use rewrite::{Rewrite, RewriteContext};
|
||||
use lists::{write_list, itemize_list, ListFormatting, SeparatorTactic, ListTactic};
|
||||
use string::{StringFormat, rewrite_string};
|
||||
use utils::span_after;
|
||||
|
||||
use syntax::{ast, ptr};
|
||||
use syntax::codemap::{Pos, Span};
|
||||
use syntax::codemap::{Pos, Span, BytePos};
|
||||
use syntax::parse::token;
|
||||
use syntax::print::pprust;
|
||||
|
||||
use MIN_STRING;
|
||||
|
||||
impl Rewrite for ast::Expr {
|
||||
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
|
||||
match self.node {
|
||||
@ -33,20 +32,22 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti
|
||||
}
|
||||
}
|
||||
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) => {
|
||||
return rewrite_paren(context, subexpr, width, offset);
|
||||
}
|
||||
ast::Expr_::ExprStruct(ref path, ref fields, ref base) => {
|
||||
return rewrite_struct_lit(context, path,
|
||||
fields,
|
||||
base.as_ref().map(|e| &**e),
|
||||
width,
|
||||
offset);
|
||||
return rewrite_struct_lit(context,
|
||||
path,
|
||||
fields,
|
||||
base.as_ref().map(|e| &**e),
|
||||
self.span,
|
||||
width,
|
||||
offset);
|
||||
}
|
||||
ast::Expr_::ExprTup(ref items) => {
|
||||
return rewrite_tuple_lit(context, items, width, offset);
|
||||
return rewrite_tuple_lit(context, items, self.span, width, offset);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -55,9 +56,12 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti
|
||||
}
|
||||
}
|
||||
|
||||
fn rewrite_string_lit(context: &RewriteContext, s: &str, span: Span, width: usize, offset: usize) -> Option<String> {
|
||||
// FIXME I bet this stomps unicode escapes in the source string
|
||||
|
||||
fn rewrite_string_lit(context: &RewriteContext,
|
||||
s: &str,
|
||||
span: Span,
|
||||
width: usize,
|
||||
offset: usize)
|
||||
-> Option<String> {
|
||||
// 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.
|
||||
let l_loc = context.codemap.lookup_char_pos(span.lo);
|
||||
@ -65,102 +69,65 @@ 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 {
|
||||
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)
|
||||
|
||||
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)
|
||||
Some(rewrite_string(&s.escape_default(), &fmt))
|
||||
}
|
||||
|
||||
fn rewrite_call(context: &RewriteContext,
|
||||
callee: &ast::Expr,
|
||||
args: &[ptr::P<ast::Expr>],
|
||||
span: Span,
|
||||
width: usize,
|
||||
offset: usize)
|
||||
-> Option<String>
|
||||
{
|
||||
-> Option<String> {
|
||||
debug!("rewrite_call, width: {}, offset: {}", width, offset);
|
||||
|
||||
// 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));
|
||||
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.
|
||||
let remaining_width = width - callee_str.len() - 2;
|
||||
let offset = callee_str.len() + 1 + offset;
|
||||
let arg_count = args.len();
|
||||
|
||||
let args_str = if arg_count > 0 {
|
||||
let args_rewritten: Vec<_> =
|
||||
try_opt!(args.iter()
|
||||
.map(|arg| arg.rewrite(context, remaining_width, offset)
|
||||
.map(|arg_str| (arg_str, String::new())))
|
||||
.collect());
|
||||
let fmt = ListFormatting {
|
||||
tactic: ListTactic::HorizontalVertical,
|
||||
separator: ",",
|
||||
trailing_separator: SeparatorTactic::Never,
|
||||
indent: offset,
|
||||
h_width: remaining_width,
|
||||
v_width: remaining_width,
|
||||
};
|
||||
write_list(&args_rewritten, &fmt)
|
||||
} else {
|
||||
String::new()
|
||||
let items = itemize_list(context.codemap,
|
||||
Vec::new(),
|
||||
args.iter(),
|
||||
",",
|
||||
")",
|
||||
|item| item.span.lo,
|
||||
|item| item.span.hi,
|
||||
// Take old span when rewrite fails.
|
||||
|item| item.rewrite(context, remaining_width, offset)
|
||||
.unwrap_or(context.codemap.span_to_snippet(item.span)
|
||||
.unwrap()),
|
||||
callee.span.hi + BytePos(1),
|
||||
span.hi);
|
||||
|
||||
let fmt = ListFormatting {
|
||||
tactic: ListTactic::HorizontalVertical,
|
||||
separator: ",",
|
||||
trailing_separator: SeparatorTactic::Never,
|
||||
indent: offset,
|
||||
h_width: remaining_width,
|
||||
v_width: remaining_width,
|
||||
ends_with_newline: 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> {
|
||||
@ -172,34 +139,68 @@ fn rewrite_paren(context: &RewriteContext, subexpr: &ast::Expr, width: usize, of
|
||||
subexpr_str.map(|s| format!("({})", s))
|
||||
}
|
||||
|
||||
fn rewrite_struct_lit(context: &RewriteContext,
|
||||
path: &ast::Path,
|
||||
fields: &[ast::Field],
|
||||
base: Option<&ast::Expr>,
|
||||
width: usize,
|
||||
offset: usize)
|
||||
fn rewrite_struct_lit<'a>(context: &RewriteContext,
|
||||
path: &ast::Path,
|
||||
fields: &'a [ast::Field],
|
||||
base: Option<&'a ast::Expr>,
|
||||
span: Span,
|
||||
width: usize,
|
||||
offset: usize)
|
||||
-> Option<String>
|
||||
{
|
||||
debug!("rewrite_struct_lit: width {}, offset {}", width, offset);
|
||||
assert!(fields.len() > 0 || base.is_some());
|
||||
|
||||
enum StructLitField<'a> {
|
||||
Regular(&'a ast::Field),
|
||||
Base(&'a ast::Expr)
|
||||
}
|
||||
|
||||
let path_str = pprust::path_to_string(path);
|
||||
// Foo { a: Foo } - indent is +3, width is -5.
|
||||
let indent = offset + path_str.len() + 3;
|
||||
let budget = width - (path_str.len() + 5);
|
||||
|
||||
let field_strs: Vec<_> =
|
||||
try_opt!(fields.iter()
|
||||
.map(|field| rewrite_field(context, field, budget, indent))
|
||||
.chain(base.iter()
|
||||
.map(|expr| expr.rewrite(context,
|
||||
// 2 = ".."
|
||||
budget - 2,
|
||||
indent + 2)
|
||||
.map(|s| format!("..{}", s))))
|
||||
.collect());
|
||||
// FIXME comments
|
||||
let field_strs: Vec<_> = field_strs.into_iter().map(|s| (s, String::new())).collect();
|
||||
let field_iter = fields.into_iter().map(StructLitField::Regular)
|
||||
.chain(base.into_iter().map(StructLitField::Base));
|
||||
|
||||
let items = itemize_list(context.codemap,
|
||||
Vec::new(),
|
||||
field_iter,
|
||||
",",
|
||||
"}",
|
||||
|item| {
|
||||
match *item {
|
||||
StructLitField::Regular(ref field) => field.span.lo,
|
||||
// 2 = ..
|
||||
StructLitField::Base(ref expr) => expr.span.lo - BytePos(2)
|
||||
}
|
||||
},
|
||||
|item| {
|
||||
match *item {
|
||||
StructLitField::Regular(ref field) => field.span.hi,
|
||||
StructLitField::Base(ref expr) => expr.span.hi
|
||||
}
|
||||
},
|
||||
|item| {
|
||||
match *item {
|
||||
StructLitField::Regular(ref field) => {
|
||||
rewrite_field(context, &field, budget, indent)
|
||||
.unwrap_or(context.codemap.span_to_snippet(field.span)
|
||||
.unwrap())
|
||||
},
|
||||
StructLitField::Base(ref expr) => {
|
||||
// 2 = ..
|
||||
expr.rewrite(context, budget - 2, indent + 2)
|
||||
.map(|s| format!("..{}", s))
|
||||
.unwrap_or(context.codemap.span_to_snippet(expr.span)
|
||||
.unwrap())
|
||||
}
|
||||
}
|
||||
},
|
||||
span_after(span, "{", context.codemap),
|
||||
span.hi);
|
||||
|
||||
let fmt = ListFormatting {
|
||||
tactic: ListTactic::HorizontalVertical,
|
||||
separator: ",",
|
||||
@ -211,14 +212,15 @@ fn rewrite_struct_lit(context: &RewriteContext,
|
||||
indent: indent,
|
||||
h_width: budget,
|
||||
v_width: budget,
|
||||
ends_with_newline: true,
|
||||
};
|
||||
let fields_str = write_list(&field_strs, &fmt);
|
||||
let fields_str = write_list(&items, &fmt);
|
||||
Some(format!("{} {{ {} }}", path_str, fields_str))
|
||||
|
||||
// FIXME if the usual multi-line layout is too wide, we should fall back to
|
||||
// Foo {
|
||||
// a: ...,
|
||||
// }
|
||||
// FIXME if the usual multi-line layout is too wide, we should fall back to
|
||||
// Foo {
|
||||
// a: ...,
|
||||
// }
|
||||
}
|
||||
|
||||
fn rewrite_field(context: &RewriteContext, field: &ast::Field, width: usize, offset: usize) -> Option<String> {
|
||||
@ -230,43 +232,43 @@ fn rewrite_field(context: &RewriteContext, field: &ast::Field, width: usize, off
|
||||
|
||||
fn rewrite_tuple_lit(context: &RewriteContext,
|
||||
items: &[ptr::P<ast::Expr>],
|
||||
span: Span,
|
||||
width: usize,
|
||||
offset: usize)
|
||||
-> Option<String> {
|
||||
// opening paren
|
||||
let indent = offset + 1;
|
||||
// In case of length 1, need a trailing comma
|
||||
if items.len() == 1 {
|
||||
return items[0].rewrite(context, width - 3, indent).map(|s| format!("({},)", s));
|
||||
}
|
||||
// Only last line has width-1 as budget, other may take max_width
|
||||
let item_strs: Vec<_> =
|
||||
try_opt!(items.iter()
|
||||
.enumerate()
|
||||
.map(|(i, item)| {
|
||||
let rem_width = if i == items.len() - 1 {
|
||||
width - 2
|
||||
} else {
|
||||
context.config.max_width - indent - 2
|
||||
};
|
||||
item.rewrite(context, rem_width, indent)
|
||||
})
|
||||
.collect());
|
||||
let tactics = if item_strs.iter().any(|s| s.contains('\n')) {
|
||||
ListTactic::Vertical
|
||||
} else {
|
||||
ListTactic::HorizontalVertical
|
||||
};
|
||||
// FIXME handle comments
|
||||
let item_strs: Vec<_> = item_strs.into_iter().map(|s| (s, String::new())).collect();
|
||||
let fmt = ListFormatting {
|
||||
tactic: tactics,
|
||||
separator: ",",
|
||||
trailing_separator: SeparatorTactic::Never,
|
||||
indent: indent,
|
||||
h_width: width - 2,
|
||||
v_width: width - 2,
|
||||
};
|
||||
let item_str = write_list(&item_strs, &fmt);
|
||||
Some(format!("({})", item_str))
|
||||
}
|
||||
let indent = offset + 1;
|
||||
|
||||
let items = itemize_list(context.codemap,
|
||||
Vec::new(),
|
||||
items.into_iter(),
|
||||
",",
|
||||
")",
|
||||
|item| item.span.lo,
|
||||
|item| item.span.hi,
|
||||
|item| item.rewrite(context,
|
||||
context.config.max_width - indent - 2,
|
||||
indent)
|
||||
.unwrap_or(context.codemap.span_to_snippet(item.span)
|
||||
.unwrap()),
|
||||
span.lo + BytePos(1), // Remove parens
|
||||
span.hi - BytePos(1));
|
||||
|
||||
// In case of length 1, need a trailing comma
|
||||
let trailing_separator_tactic = if items.len() == 1 {
|
||||
SeparatorTactic::Always
|
||||
} else {
|
||||
SeparatorTactic::Never
|
||||
};
|
||||
|
||||
let fmt = ListFormatting {
|
||||
tactic: ListTactic::HorizontalVertical,
|
||||
separator: ",",
|
||||
trailing_separator: trailing_separator_tactic,
|
||||
indent: indent,
|
||||
h_width: width - 2,
|
||||
v_width: width - 2,
|
||||
ends_with_newline: true,
|
||||
};
|
||||
|
||||
Some(format!("({})", write_list(&items, &fmt)))
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
// except according to those terms.
|
||||
|
||||
use visitor::FmtVisitor;
|
||||
use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};
|
||||
use lists::{write_list, ListItem, ListFormatting, SeparatorTactic, ListTactic};
|
||||
use utils::format_visibility;
|
||||
|
||||
use syntax::ast;
|
||||
@ -65,16 +65,8 @@ pub fn rewrite_use_list(&mut self,
|
||||
let used_width = indent + 2;
|
||||
|
||||
// Break as early as possible when we've blown our budget.
|
||||
let remaining_line_budget = if used_width > one_line_budget {
|
||||
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 remaining_line_budget = one_line_budget.checked_sub(used_width).unwrap_or(0);
|
||||
let remaining_multi_budget = multi_line_budget.checked_sub(used_width).unwrap_or(0);
|
||||
|
||||
let fmt = ListFormatting {
|
||||
tactic: ListTactic::Mixed,
|
||||
@ -83,6 +75,7 @@ pub fn rewrite_use_list(&mut self,
|
||||
indent: block_indent + indent,
|
||||
h_width: remaining_line_budget,
|
||||
v_width: remaining_multi_budget,
|
||||
ends_with_newline: true,
|
||||
};
|
||||
|
||||
// TODO handle any comments inbetween items.
|
||||
@ -94,7 +87,7 @@ pub fn rewrite_use_list(&mut self,
|
||||
false
|
||||
}
|
||||
) {
|
||||
Some(("self".to_owned(), String::new()))
|
||||
Some(ListItem::from_str("self"))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@ -102,12 +95,13 @@ pub fn rewrite_use_list(&mut self,
|
||||
let items: Vec<_> = head.into_iter().chain(path_list.iter().filter_map(|vpi| {
|
||||
match vpi.node {
|
||||
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.
|
||||
ast::PathListItem_::PathListMod{ .. } => None,
|
||||
}
|
||||
})).collect();
|
||||
|
||||
Some(if path_str.len() == 0 {
|
||||
format!("{}use {{{}}};", vis, write_list(&items, &fmt))
|
||||
} else {
|
||||
|
@ -223,7 +223,6 @@ fn inspect_number(&mut self,
|
||||
#[test]
|
||||
fn find_unnumbered_issue() {
|
||||
fn check_fail(text: &str, failing_pos: usize) {
|
||||
println!("{:?}", text);
|
||||
let mut seeker = BadIssueSeeker::new(ReportTactic::Unnumbered, ReportTactic::Unnumbered);
|
||||
assert_eq!(Some(failing_pos), text.chars().position(|c| seeker.inspect(c).is_some()));
|
||||
}
|
||||
|
508
src/items.rs
508
src/items.rs
@ -11,9 +11,11 @@
|
||||
// Formatting top-level items - functions, structs, enums, traits, impls.
|
||||
|
||||
use {ReturnIndent, BraceStyle};
|
||||
use utils::{format_visibility, make_indent, FindUncommented};
|
||||
use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};
|
||||
use utils::{format_visibility, make_indent, contains_skip, span_after};
|
||||
use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic, ListTactic};
|
||||
use comment::FindUncommented;
|
||||
use visitor::FmtVisitor;
|
||||
|
||||
use syntax::{ast, abi};
|
||||
use syntax::codemap::{self, Span, BytePos};
|
||||
use syntax::print::pprust;
|
||||
@ -30,7 +32,7 @@ pub fn rewrite_fn(&mut self,
|
||||
constness: &ast::Constness,
|
||||
abi: &abi::Abi,
|
||||
vis: ast::Visibility,
|
||||
span_end: BytePos)
|
||||
span: Span)
|
||||
-> String
|
||||
{
|
||||
let newline_brace = self.newline_for_brace(&generics.where_clause);
|
||||
@ -44,7 +46,7 @@ pub fn rewrite_fn(&mut self,
|
||||
constness,
|
||||
abi,
|
||||
vis,
|
||||
span_end,
|
||||
span,
|
||||
newline_brace);
|
||||
|
||||
// Prepare for the function body by possibly adding a newline and indent.
|
||||
@ -68,7 +70,7 @@ pub fn rewrite_required_fn(&mut self,
|
||||
-> String
|
||||
{
|
||||
// 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,
|
||||
ident,
|
||||
@ -79,7 +81,7 @@ pub fn rewrite_required_fn(&mut self,
|
||||
&sig.constness,
|
||||
&sig.abi,
|
||||
ast::Visibility::Inherited,
|
||||
span_end,
|
||||
span,
|
||||
false);
|
||||
|
||||
// Re-attach semicolon
|
||||
@ -98,7 +100,7 @@ fn rewrite_fn_base(&mut self,
|
||||
constness: &ast::Constness,
|
||||
abi: &abi::Abi,
|
||||
vis: ast::Visibility,
|
||||
span_end: BytePos,
|
||||
span: Span,
|
||||
newline_brace: bool)
|
||||
-> String
|
||||
{
|
||||
@ -131,7 +133,8 @@ fn rewrite_fn_base(&mut self,
|
||||
let generics_indent = indent + result.len();
|
||||
result.push_str(&self.rewrite_generics(generics,
|
||||
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);
|
||||
|
||||
@ -162,7 +165,8 @@ fn rewrite_fn_base(&mut self,
|
||||
one_line_budget,
|
||||
multi_line_budget,
|
||||
arg_indent,
|
||||
span_for_return(&fd.output)));
|
||||
codemap::mk_sp(span_after(span, "(", self.codemap),
|
||||
span_for_return(&fd.output).lo)));
|
||||
result.push(')');
|
||||
|
||||
// Return type.
|
||||
@ -189,7 +193,7 @@ fn rewrite_fn_base(&mut self,
|
||||
// Comment between return type and the end of the decl.
|
||||
let snippet_lo = fd.output.span().hi;
|
||||
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 = snippet.trim();
|
||||
if snippet.len() > 0 {
|
||||
@ -204,7 +208,9 @@ fn rewrite_fn_base(&mut self,
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
@ -215,7 +221,7 @@ fn rewrite_args(&self,
|
||||
one_line_budget: usize,
|
||||
multi_line_budget: usize,
|
||||
arg_indent: usize,
|
||||
ret_span: Span)
|
||||
span: Span)
|
||||
-> String
|
||||
{
|
||||
let mut arg_item_strs: Vec<_> = args.iter().map(|a| self.rewrite_fn_input(a)).collect();
|
||||
@ -262,89 +268,50 @@ fn rewrite_args(&self,
|
||||
}
|
||||
|
||||
// Comments between args
|
||||
let mut arg_comments = Vec::new();
|
||||
let mut arg_items = Vec::new();
|
||||
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
|
||||
// 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.
|
||||
if args.len() >= min_args {
|
||||
arg_comments = self.make_comments_for_list(arg_comments,
|
||||
args[min_args-1..].iter(),
|
||||
",",
|
||||
")",
|
||||
|arg| arg.pat.span.lo,
|
||||
|arg| arg.ty.span.hi,
|
||||
ret_span.lo);
|
||||
let comment_span_start = if min_args == 2 {
|
||||
span_after(span, ",", self.codemap)
|
||||
} else {
|
||||
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.
|
||||
let mut list_tactic = ListTactic::HorizontalVertical;
|
||||
if arg_comments.iter().any(|c| c.contains("//")) {
|
||||
list_tactic = ListTactic::Vertical;
|
||||
for (item, arg) in arg_items.iter_mut().zip(arg_item_strs) {
|
||||
item.item = arg;
|
||||
}
|
||||
|
||||
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 {
|
||||
tactic: list_tactic,
|
||||
tactic: ListTactic::HorizontalVertical,
|
||||
separator: ",",
|
||||
trailing_separator: SeparatorTactic::Never,
|
||||
indent: arg_indent,
|
||||
h_width: one_line_budget,
|
||||
v_width: multi_line_budget,
|
||||
ends_with_newline: true,
|
||||
};
|
||||
|
||||
write_list(&arg_strs, &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
|
||||
write_list(&arg_items, &fmt)
|
||||
}
|
||||
|
||||
fn compute_budgets_for_args(&self,
|
||||
@ -412,12 +379,16 @@ pub fn visit_enum(&mut self,
|
||||
generics: &ast::Generics,
|
||||
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);
|
||||
|
||||
let enum_snippet = self.snippet(span);
|
||||
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.last_pos = body_start;
|
||||
@ -447,75 +418,185 @@ fn visit_variant(&mut self,
|
||||
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);
|
||||
self.changes.push_str_span(field.span, vis);
|
||||
let name = field.node.name.to_string();
|
||||
self.changes.push_str_span(field.span, &name);
|
||||
match field.node.kind {
|
||||
ast::VariantKind::TupleVariantKind(ref types) => {
|
||||
let vis = format_visibility(field.node.vis);
|
||||
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 {
|
||||
let comments = self.make_comments_for_list(Vec::new(),
|
||||
types.iter().map(|arg| arg.ty.span),
|
||||
",",
|
||||
")",
|
||||
|span| span.lo,
|
||||
|span| span.hi,
|
||||
next_span_start);
|
||||
if types.len() > 0 {
|
||||
let items = itemize_list(self.codemap,
|
||||
Vec::new(),
|
||||
types.iter(),
|
||||
",",
|
||||
")",
|
||||
|arg| arg.ty.span.lo,
|
||||
|arg| arg.ty.span.hi,
|
||||
|arg| pprust::ty_to_string(&arg.ty),
|
||||
span_after(field.span, "(", self.codemap),
|
||||
next_span_start);
|
||||
|
||||
let type_strings: Vec<_> = types.iter()
|
||||
.map(|arg| pprust::ty_to_string(&arg.ty))
|
||||
.zip(comments.into_iter())
|
||||
.collect();
|
||||
result.push('(');
|
||||
|
||||
result.push('(');
|
||||
let indent = self.block_indent
|
||||
+ vis.len()
|
||||
+ field.node.name.to_string().len()
|
||||
+ 1; // Open paren
|
||||
|
||||
let indent = self.block_indent
|
||||
+ vis.len()
|
||||
+ field.node.name.to_string().len()
|
||||
+ 1; // 1 = (
|
||||
let comma_cost = if self.config.enum_trailing_comma { 1 } else { 0 };
|
||||
let budget = self.config.ideal_width - indent - comma_cost - 1; // 1 = )
|
||||
|
||||
let comma_cost = if self.config.enum_trailing_comma { 1 } else { 0 };
|
||||
let budget = self.config.ideal_width - indent - comma_cost - 1; // 1 = )
|
||||
let fmt = ListFormatting {
|
||||
tactic: ListTactic::HorizontalVertical,
|
||||
separator: ",",
|
||||
trailing_separator: SeparatorTactic::Never,
|
||||
indent: indent,
|
||||
h_width: budget,
|
||||
v_width: budget,
|
||||
ends_with_newline: false,
|
||||
};
|
||||
result.push_str(&write_list(&items, &fmt));
|
||||
result.push(')');
|
||||
}
|
||||
|
||||
let fmt = ListFormatting {
|
||||
tactic: ListTactic::HorizontalVertical,
|
||||
separator: ",",
|
||||
trailing_separator: SeparatorTactic::Never,
|
||||
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 {
|
||||
result.push_str(" = ");
|
||||
let expr_snippet = self.snippet(expr.span);
|
||||
result.push_str(&expr_snippet);
|
||||
|
||||
if let Some(ref expr) = field.node.disr_expr {
|
||||
result.push_str(" = ");
|
||||
let expr_snippet = self.snippet(expr.span);
|
||||
result.push_str(&expr_snippet);
|
||||
// Make sure we do not exceed column limit
|
||||
// 4 = " = ,"
|
||||
assert!(self.config.max_width >= vis.len() + name.len() + expr_snippet.len() + 4,
|
||||
"Enum variant exceeded column limit");
|
||||
}
|
||||
|
||||
// Make sure we do not exceed column limit
|
||||
// 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);
|
||||
|
||||
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, ",");
|
||||
self.changes.push_str_span(field.span, &result)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: deal with struct-like variants
|
||||
|
||||
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),
|
||||
span_after(span, opener.trim(), self.codemap),
|
||||
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,
|
||||
ends_with_newline: 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,
|
||||
ident: ast::Ident,
|
||||
vis: ast::Visibility,
|
||||
@ -523,34 +604,16 @@ pub fn visit_struct(&mut self,
|
||||
generics: &ast::Generics,
|
||||
span: Span)
|
||||
{
|
||||
let header_str = self.format_header("struct", ident, vis);
|
||||
self.changes.push_str_span(span, &header_str);
|
||||
|
||||
if struct_def.fields.len() == 0 {
|
||||
assert!(generics.where_clause.predicates.len() == 0,
|
||||
"No-field struct with where clause?");
|
||||
assert!(generics.lifetimes.len() == 0, "No-field struct with generics?");
|
||||
assert!(generics.ty_params.len() == 0, "No-field struct with generics?");
|
||||
|
||||
self.changes.push_str_span(span, ";");
|
||||
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, "}");
|
||||
let indent = self.block_indent;
|
||||
let result = self.format_struct("struct ",
|
||||
ident,
|
||||
vis,
|
||||
struct_def,
|
||||
Some(generics),
|
||||
span,
|
||||
indent);
|
||||
self.changes.push_str_span(span, &result);
|
||||
self.last_pos = span.hi;
|
||||
}
|
||||
|
||||
fn format_header(&self,
|
||||
@ -559,42 +622,37 @@ fn format_header(&self,
|
||||
vis: ast::Visibility)
|
||||
-> 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,
|
||||
generics: &ast::Generics,
|
||||
span_end: BytePos)
|
||||
opener: &str,
|
||||
offset: usize,
|
||||
span: Span)
|
||||
-> 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,
|
||||
self.block_indent,
|
||||
span_end));
|
||||
self.block_indent,
|
||||
span.hi));
|
||||
result.push_str(&make_indent(self.block_indent));
|
||||
result.push_str("\n{");
|
||||
|
||||
result.push('\n');
|
||||
result.push_str(opener.trim());
|
||||
} else {
|
||||
result.push_str(" {");
|
||||
result.push_str(opener);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
// Field of a struct
|
||||
fn visit_field(&mut self,
|
||||
field: &ast::StructField,
|
||||
last_field: bool,
|
||||
// These two args are for missing spans hacks.
|
||||
struct_start: BytePos,
|
||||
struct_snippet: &str)
|
||||
{
|
||||
if self.visit_attrs(&field.node.attrs) {
|
||||
return;
|
||||
fn format_field(&self, field: &ast::StructField) -> String {
|
||||
if contains_skip(&field.node.attrs) {
|
||||
return self.snippet(codemap::mk_sp(field.node.attrs[0].span.lo, field.span.hi));
|
||||
}
|
||||
self.format_missing_with_indent(field.span.lo);
|
||||
|
||||
let name = match field.node.kind {
|
||||
ast::StructFieldKind::NamedField(ident, _) => Some(token::get_ident(ident)),
|
||||
@ -606,38 +664,20 @@ fn visit_field(&mut self,
|
||||
};
|
||||
let typ = pprust::ty_to_string(&field.node.ty);
|
||||
|
||||
let mut field_str = match name {
|
||||
Some(name) => {
|
||||
let budget = self.config.ideal_width - self.block_indent;
|
||||
// 3 is being conservative and assuming that there will be a trailing comma.
|
||||
if self.block_indent + vis.len() + name.len() + typ.len() + 3 > budget {
|
||||
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(',');
|
||||
let indent = self.block_indent + self.config.tab_spaces;
|
||||
let mut attr_str = self.rewrite_attrs(&field.node.attrs, indent);
|
||||
if attr_str.len() > 0 {
|
||||
attr_str.push('\n');
|
||||
attr_str.push_str(&make_indent(indent));
|
||||
}
|
||||
self.changes.push_str_span(field.span, &field_str);
|
||||
|
||||
// This hack makes sure we only add comments etc. after the comma, and
|
||||
// makes sure we don't repeat any commas.
|
||||
let hi = field.span.hi;
|
||||
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);
|
||||
match name {
|
||||
Some(name) => format!("{}{}{}: {}", attr_str, vis, name, typ),
|
||||
None => format!("{}{}{}", attr_str, vis, typ)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// there is a where clause at all.
|
||||
let mut result = String::new();
|
||||
@ -647,7 +687,7 @@ fn rewrite_generics(&self, generics: &ast::Generics, indent: usize, span_end: By
|
||||
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
|
||||
result.push('<');
|
||||
|
||||
@ -665,30 +705,32 @@ fn rewrite_generics(&self, generics: &ast::Generics, indent: usize, span_end: By
|
||||
codemap::mk_sp(l.lifetime.span.lo, hi)
|
||||
});
|
||||
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 list_tactic = ListTactic::HorizontalVertical;
|
||||
if comments.iter().any(|c| c.contains("//")) {
|
||||
list_tactic = ListTactic::Vertical;
|
||||
let mut items = itemize_list(self.codemap,
|
||||
Vec::new(),
|
||||
lt_spans.chain(ty_spans),
|
||||
",",
|
||||
">",
|
||||
|sp| sp.lo,
|
||||
|sp| sp.hi,
|
||||
|_| String::new(),
|
||||
span_after(span, "<", self.codemap),
|
||||
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 {
|
||||
tactic: list_tactic,
|
||||
tactic: ListTactic::HorizontalVertical,
|
||||
separator: ",",
|
||||
trailing_separator: SeparatorTactic::Never,
|
||||
indent: indent + 1,
|
||||
indent: offset + 1,
|
||||
h_width: budget,
|
||||
v_width: budget,
|
||||
ends_with_newline: true,
|
||||
};
|
||||
result.push_str(&write_list(&generics_strs, &fmt));
|
||||
result.push_str(&write_list(&items, &fmt));
|
||||
|
||||
result.push('>');
|
||||
|
||||
@ -710,18 +752,17 @@ fn rewrite_where_clause(&self,
|
||||
result.push_str(&make_indent(indent + 4));
|
||||
result.push_str("where ");
|
||||
|
||||
let comments = self.make_comments_for_list(Vec::new(),
|
||||
where_clause.predicates.iter(),
|
||||
",",
|
||||
"{",
|
||||
|pred| span_for_where_pred(pred).lo,
|
||||
|pred| span_for_where_pred(pred).hi,
|
||||
span_end);
|
||||
|
||||
let where_strs: Vec<_> = where_clause.predicates.iter()
|
||||
.map(|p| (self.rewrite_pred(p)))
|
||||
.zip(comments.into_iter())
|
||||
.collect();
|
||||
let span_start = span_for_where_pred(&where_clause.predicates[0]).lo;
|
||||
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,
|
||||
|pred| self.rewrite_pred(pred),
|
||||
span_start,
|
||||
span_end);
|
||||
|
||||
let budget = self.config.ideal_width + self.config.leeway - indent - 10;
|
||||
let fmt = ListFormatting {
|
||||
@ -731,8 +772,9 @@ fn rewrite_where_clause(&self,
|
||||
indent: indent + 10,
|
||||
h_width: budget,
|
||||
v_width: budget,
|
||||
ends_with_newline: true,
|
||||
};
|
||||
result.push_str(&write_list(&where_strs, &fmt));
|
||||
result.push_str(&write_list(&items, &fmt));
|
||||
|
||||
result
|
||||
}
|
||||
|
@ -62,6 +62,8 @@
|
||||
mod imports;
|
||||
mod issues;
|
||||
mod rewrite;
|
||||
mod string;
|
||||
mod comment;
|
||||
|
||||
const MIN_STRING: usize = 10;
|
||||
// When we get scoped annotations, we should have rustfmt::skip.
|
||||
|
242
src/lists.rs
242
src/lists.rs
@ -8,7 +8,13 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// 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)]
|
||||
pub enum ListTactic {
|
||||
@ -41,11 +47,38 @@ pub struct ListFormatting<'a> {
|
||||
pub h_width: usize,
|
||||
// Available width if we layout vertically
|
||||
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 ends_with_newline: bool
|
||||
}
|
||||
|
||||
// Format a list of strings into a string.
|
||||
// Precondition: all strings in items are trimmed.
|
||||
pub fn write_list<'b>(items: &[(String, String)], formatting: &ListFormatting<'b>) -> String {
|
||||
pub struct ListItem {
|
||||
pub pre_comment: Option<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 {
|
||||
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: {}",
|
||||
total_width, total_sep_len, formatting.h_width);
|
||||
tactic = if fits_single &&
|
||||
!items.iter().any(|&(ref s, _)| s.contains('\n')) {
|
||||
!items.iter().any(ListItem::is_multiline) {
|
||||
ListTactic::Horizontal
|
||||
} else {
|
||||
ListTactic::Vertical
|
||||
@ -81,6 +114,11 @@ pub fn write_list<'b>(items: &[(String, String)], formatting: &ListFormatting<'b
|
||||
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
|
||||
// will be a trailing separator.
|
||||
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 {
|
||||
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 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 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 {
|
||||
ListTactic::Horizontal if !first => {
|
||||
@ -109,12 +150,9 @@ pub fn write_list<'b>(items: &[(String, String)], formatting: &ListFormatting<'b
|
||||
result.push_str(indent_str);
|
||||
}
|
||||
ListTactic::Mixed => {
|
||||
let mut item_width = item.len();
|
||||
if separate {
|
||||
item_width += sep_len;
|
||||
}
|
||||
let total_width = total_item_width(item) + item_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_str(indent_str);
|
||||
line_len = 0;
|
||||
@ -125,35 +163,160 @@ pub fn write_list<'b>(items: &[(String, String)], formatting: &ListFormatting<'b
|
||||
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,
|
||||
// Width restriction is only
|
||||
// relevant in vertical mode.
|
||||
formatting.v_width,
|
||||
formatting.indent));
|
||||
|
||||
if tactic != ListTactic::Vertical && comment.len() > 0 {
|
||||
if !comment.starts_with('\n') {
|
||||
if tactic == ListTactic::Vertical {
|
||||
result.push('\n');
|
||||
result.push_str(indent_str);
|
||||
} else {
|
||||
result.push(' ');
|
||||
}
|
||||
result.push_str(comment);
|
||||
}
|
||||
|
||||
result.push_str(&item.item);
|
||||
|
||||
// Post-comments
|
||||
if tactic != ListTactic::Vertical && item.post_comment.is_some() {
|
||||
let formatted_comment = rewrite_comment(item.post_comment.as_ref().unwrap(),
|
||||
true,
|
||||
formatting.v_width,
|
||||
0);
|
||||
|
||||
result.push(' ');
|
||||
result.push_str(&formatted_comment);
|
||||
}
|
||||
|
||||
if separate {
|
||||
result.push_str(formatting.separator);
|
||||
}
|
||||
|
||||
if tactic == ListTactic::Vertical && comment.len() > 0 {
|
||||
if !comment.starts_with('\n') {
|
||||
result.push(' ');
|
||||
}
|
||||
result.push_str(comment);
|
||||
if tactic == ListTactic::Vertical && item.post_comment.is_some() {
|
||||
// 1 = space between item and comment.
|
||||
let width = formatting.v_width.checked_sub(item_width + 1).unwrap_or(1);
|
||||
let offset = formatting.indent + item_width + 1;
|
||||
let comment = item.post_comment.as_ref().unwrap();
|
||||
// Use block-style only for the last item or multiline comments.
|
||||
let block_style = formatting.ends_with_newline && 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
|
||||
}
|
||||
|
||||
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_string: 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_string(&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 {
|
||||
match separator_tactic {
|
||||
SeparatorTactic::Always => true,
|
||||
@ -162,16 +325,25 @@ fn needs_trailing_separator(separator_tactic: SeparatorTactic, list_tactic: List
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_width(items:&[(String, String)]) -> usize {
|
||||
let missed_width = items.iter().map(|&(_, ref s)| {
|
||||
let text_len = s.trim().len();
|
||||
if text_len > 0 {
|
||||
// We'll put a space before any comment.
|
||||
text_len + 1
|
||||
} else {
|
||||
text_len
|
||||
}
|
||||
}).fold(0, |a, l| a + l);
|
||||
let item_width = items.iter().map(|&(ref s, _)| s.len()).fold(0, |a, l| a + l);
|
||||
missed_width + item_width
|
||||
fn calculate_width(items: &[ListItem]) -> usize {
|
||||
items.iter().map(total_item_width).fold(0, |a, l| a + l)
|
||||
}
|
||||
|
||||
fn total_item_width(item: &ListItem) -> usize {
|
||||
comment_len(&item.pre_comment) + comment_len(&item.post_comment) + item.item.len()
|
||||
}
|
||||
|
||||
fn comment_len(comment: &Option<String>) -> usize {
|
||||
match comment {
|
||||
&Some(ref s) => {
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
82
src/utils.rs
82
src/utils.rs
@ -8,72 +8,18 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use syntax::ast::Visibility;
|
||||
use syntax::ast::{Visibility, Attribute, MetaItem, MetaItem_};
|
||||
use syntax::codemap::{CodeMap, Span, BytePos};
|
||||
|
||||
pub trait FindUncommented {
|
||||
fn find_uncommented(&self, pat: &str) -> Option<usize>;
|
||||
}
|
||||
use comment::FindUncommented;
|
||||
|
||||
impl FindUncommented for str {
|
||||
fn find_uncommented(&self, pat: &str) -> Option<usize> {
|
||||
let mut needle_iter = pat.chars();
|
||||
let mut possible_comment = false;
|
||||
use SKIP_ANNOTATION;
|
||||
|
||||
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())
|
||||
}
|
||||
#[inline]
|
||||
pub fn span_after(original: Span, needle: &str, codemap: &CodeMap) -> BytePos {
|
||||
let snippet = codemap.span_to_snippet(original).unwrap();
|
||||
|
||||
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);
|
||||
original.lo + BytePos(snippet.find_uncommented(needle).unwrap() as u32 + 1)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -114,6 +60,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]
|
||||
#[cfg(target_pointer_width="64")]
|
||||
// Based on the trick layed out at
|
||||
|
@ -15,7 +15,6 @@
|
||||
use utils;
|
||||
use config::Config;
|
||||
|
||||
use SKIP_ANNOTATION;
|
||||
use changes::ChangeSet;
|
||||
use rewrite::{Rewrite, RewriteContext};
|
||||
|
||||
@ -120,7 +119,7 @@ fn visit_fn(&mut self,
|
||||
constness,
|
||||
abi,
|
||||
vis,
|
||||
b.span.lo);
|
||||
codemap::mk_sp(s.lo, b.span.lo));
|
||||
self.changes.push_str_span(s, &new_fn);
|
||||
}
|
||||
visit::FkMethod(ident, ref sig, vis) => {
|
||||
@ -133,7 +132,7 @@ fn visit_fn(&mut self,
|
||||
&sig.constness,
|
||||
&sig.abi,
|
||||
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);
|
||||
}
|
||||
visit::FkFnBlock(..) => {}
|
||||
@ -305,26 +304,22 @@ pub fn visit_attrs(&mut self, attrs: &[ast::Attribute]) -> bool {
|
||||
let first = &attrs[0];
|
||||
self.format_missing_with_indent(first.span.lo);
|
||||
|
||||
match self.rewrite_attrs(attrs, self.block_indent) {
|
||||
Some(s) => {
|
||||
self.changes.push_str_span(first.span, &s);
|
||||
let last = attrs.last().unwrap();
|
||||
self.last_pos = last.span.hi;
|
||||
false
|
||||
}
|
||||
None => true
|
||||
if utils::contains_skip(attrs) {
|
||||
true
|
||||
} else {
|
||||
let rewrite = self.rewrite_attrs(attrs, self.block_indent);
|
||||
self.changes.push_str_span(first.span, &rewrite);
|
||||
let last = attrs.last().unwrap();
|
||||
self.last_pos = last.span.hi;
|
||||
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 indent = utils::make_indent(indent);
|
||||
|
||||
for (i, a) in attrs.iter().enumerate() {
|
||||
if is_skip(&a.node.value) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let a_str = self.snippet(a.span);
|
||||
|
||||
if i > 0 {
|
||||
@ -351,13 +346,6 @@ fn rewrite_attrs(&self, attrs: &[ast::Attribute], indent: usize) -> Option<Strin
|
||||
}
|
||||
}
|
||||
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_skip(meta_item: &ast::MetaItem) -> bool {
|
||||
match meta_item.node {
|
||||
ast::MetaItem_::MetaWord(ref s) => *s == SKIP_ANNOTATION,
|
||||
_ => false,
|
||||
result
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ newline_style = "Unix"
|
||||
fn_brace_style = "SameLineWhere"
|
||||
fn_return_indent = "WithArgs"
|
||||
fn_args_paren_newline = true
|
||||
struct_trailing_comma = true
|
||||
struct_trailing_comma = "Vertical"
|
||||
struct_lit_trailing_comma = "Vertical"
|
||||
enum_trailing_comma = true
|
||||
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.
|
||||
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 {}
|
||||
|
||||
/// Blah blah blah.
|
||||
@ -107,21 +107,3 @@ fn main() {
|
||||
let x = "Hello!!!!!!!!! abcd abcd abcd abcd abcd abcd\n abcd abcd abcd abcd abcd abcd abcd abcd abcd \
|
||||
abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd \
|
||||
abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd"; }
|
||||
|
||||
|
||||
fn struct_lits() {
|
||||
let x = Bar;
|
||||
// Comment
|
||||
let y = Foo { a: x };
|
||||
Foo { a: foo() /* comment*/, /* comment*/ b: bar(), ..something };
|
||||
Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(), b: bar(), };
|
||||
Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo {
|
||||
// Comment
|
||||
a: foo(), // Comment
|
||||
// Comment
|
||||
b: bar(), // Comment
|
||||
};
|
||||
|
||||
Foo { a: Bar,
|
||||
b: foo() };
|
||||
}
|
||||
|
37
tests/source/struct_lits.rs
Normal file
37
tests/source/struct_lits.rs
Normal file
@ -0,0 +1,37 @@
|
||||
// Struct literal expressions.
|
||||
|
||||
fn main() {
|
||||
let x = Bar;
|
||||
|
||||
// Comment
|
||||
let y = Foo {a: x };
|
||||
|
||||
Foo { a: foo() /* comment*/, /* comment*/ b: bar(), ..something };
|
||||
|
||||
Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(), b: bar(), };
|
||||
|
||||
Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo {
|
||||
// Comment
|
||||
a: foo(), // Comment
|
||||
// Comment
|
||||
b: bar(), // Comment
|
||||
};
|
||||
|
||||
Foo { a:Bar,
|
||||
b:foo() };
|
||||
|
||||
A {
|
||||
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor.
|
||||
first: item(),
|
||||
// Praesent et diam eget libero egestas mattis sit amet vitae augue.
|
||||
// Nam tincidunt congue enim, ut porta lorem lacinia consectetur.
|
||||
second: Item
|
||||
};
|
||||
|
||||
Diagram { /* o This graph demonstrates how
|
||||
* / \ significant whitespace is
|
||||
* o o preserved.
|
||||
* /|\ \
|
||||
* o o o o */
|
||||
graph: G, }
|
||||
}
|
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.
|
||||
fn foo<F, G>(a: aaaaaaaaaaaaa, // A comment
|
||||
b: bbbbbbbbbbbbb, /* a second comment */
|
||||
b: bbbbbbbbbbbbb, // a second comment
|
||||
c: ccccccccccccc,
|
||||
// Newline comment
|
||||
d: ddddddddddddd,
|
||||
// A multi line comment
|
||||
// between args.
|
||||
e: eeeeeeeeeeeee /* comment before paren*/)
|
||||
e: eeeeeeeeeeeee /* comment before paren */)
|
||||
-> bar
|
||||
where F: Foo, // COmment after where clause
|
||||
G: Goo /* final comment */
|
||||
|
@ -3,7 +3,7 @@
|
||||
#[atrr]
|
||||
pub enum Test {
|
||||
A,
|
||||
B(u32, A /* comment */),
|
||||
B(u32, A /* comment */, SomeType),
|
||||
/// Doc comment
|
||||
C,
|
||||
}
|
||||
@ -27,8 +27,18 @@ enum Bar {
|
||||
}
|
||||
|
||||
enum LongVariants {
|
||||
First(LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG, // small comment
|
||||
First(LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG, // comment
|
||||
VARIANT),
|
||||
// This is the second variant
|
||||
Second,
|
||||
}
|
||||
|
||||
enum StructLikeVariants {
|
||||
Normal(u32, String),
|
||||
StructLike {
|
||||
x: i32, // Test comment
|
||||
// Pre-comment
|
||||
#[Attr50]
|
||||
y: SomeType, // Aanother Comment
|
||||
}
|
||||
}
|
||||
|
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!()
|
||||
}
|
||||
|
||||
fn baz<'a: 'b, /* comment on 'a */
|
||||
fn baz<'a: 'b, // comment on 'a
|
||||
T: SomsssssssssssssssssssssssssssssssssssssssssssssssssssssseType /* comment on T */>
|
||||
(a: A,
|
||||
b: B, /* comment on b */
|
||||
b: B, // comment on b
|
||||
c: C)
|
||||
-> Bob {
|
||||
#[attr1]
|
||||
@ -65,8 +65,9 @@ fn qux(a: dadsfa, // Comment 1
|
||||
/// Blah blah blah.
|
||||
impl Bar {
|
||||
fn foo(&mut self,
|
||||
a: sdfsdfcccccccccccccccccccccccccccccccccccccccccccccccccccccccc, // comment on a
|
||||
b: sdfasdfsdfasfs /*closing comment*/)
|
||||
a: sdfsdfcccccccccccccccccccccccccccccccccccccccccccccccccc, /* commen
|
||||
* t on a */
|
||||
b: sdfasdfsdfasfs /* closing comment */)
|
||||
-> isize {
|
||||
}
|
||||
|
||||
@ -138,17 +139,3 @@ fn main() {
|
||||
abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd \
|
||||
abcd";
|
||||
}
|
||||
|
||||
|
||||
fn struct_lits() {
|
||||
let x = Bar;
|
||||
// Comment
|
||||
let y = Foo { a: x };
|
||||
Foo { a: foo(), b: bar(), ..something };
|
||||
Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(),
|
||||
b: bar(), };
|
||||
Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(),
|
||||
b: bar(), };
|
||||
|
||||
Foo { a: Bar, b: foo() };
|
||||
}
|
||||
|
@ -6,19 +6,45 @@ fn main() {
|
||||
// Comment
|
||||
let y = Foo { a: x };
|
||||
|
||||
Foo { a: Bar, b: foo() };
|
||||
Foo { a: foo(), // comment
|
||||
// comment
|
||||
b: bar(),
|
||||
..something };
|
||||
|
||||
Foo { a: foo(), b: bar(), ..something };
|
||||
|
||||
Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(), b: bar() };
|
||||
Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(),
|
||||
b: bar(), };
|
||||
|
||||
Fooooooooooooooooooooooooooooooooooooooooooooooooooooo { a: foo(),
|
||||
b: bar(),
|
||||
c: bar(),
|
||||
d: bar(),
|
||||
e: bar(),
|
||||
f: bar(),
|
||||
..baz() };
|
||||
Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo { // Comment
|
||||
a: foo(), /* C
|
||||
* o
|
||||
* m
|
||||
* m
|
||||
* e
|
||||
* n
|
||||
* t */
|
||||
// Comment
|
||||
b: bar(), /* C
|
||||
* o
|
||||
* m
|
||||
* m
|
||||
* e
|
||||
* n
|
||||
* t */ };
|
||||
|
||||
Foo { a: Bar, b: foo() };
|
||||
|
||||
A { // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit
|
||||
// amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante
|
||||
// hendrerit. Donec et mollis dolor.
|
||||
first: item(),
|
||||
// Praesent et diam eget libero egestas mattis sit amet vitae augue.
|
||||
// Nam tincidunt congue enim, ut porta lorem lacinia consectetur.
|
||||
second: Item, };
|
||||
|
||||
Diagram { // o This graph demonstrates how
|
||||
// / \ significant whitespace is
|
||||
// o o preserved.
|
||||
// /|\ \
|
||||
// o o o o
|
||||
graph: G, }
|
||||
}
|
||||
|
@ -15,6 +15,32 @@ pub struct Foo {
|
||||
|
||||
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
|
||||
@ -36,3 +62,6 @@ struct Baz {
|
||||
// Comment C
|
||||
c: C,
|
||||
}
|
||||
|
||||
// Will this be a one-liner?
|
||||
struct Tuple(A /* Comment */, B);
|
||||
|
@ -8,4 +8,8 @@ fn foo() {
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaa,
|
||||
aaaa);
|
||||
let a = (a,);
|
||||
|
||||
let b = (// This is a comment
|
||||
b, // Comment
|
||||
b /* Trailing comment */);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user