diff --git a/src/expr.rs b/src/expr.rs index 334c509c59d..4cfa5a0b879 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -12,14 +12,14 @@ use lists::{write_list, itemize_list, ListFormatting, SeparatorTactic, ListTactic}; use string::{StringFormat, rewrite_string}; use StructLitStyle; -use utils::{span_after, make_indent}; +use utils::{span_after, make_indent, extra_offset}; use visitor::FmtVisitor; use config::BlockIndentStyle; use comment::{FindUncommented, rewrite_comment}; +use types::rewrite_path; use syntax::{ast, ptr}; use syntax::codemap::{Pos, Span, BytePos, mk_sp}; -use syntax::print::pprust; use syntax::visit::Visitor; impl Rewrite for ast::Expr { @@ -99,6 +99,9 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti width, offset) } + ast::Expr_::ExprPath(ref qself, ref path) => { + rewrite_path(context, qself.as_ref(), path, width, offset) + } _ => context.codemap.span_to_snippet(self.span).ok() } } @@ -320,11 +323,7 @@ fn rewrite_pat_expr(context: &RewriteContext, }; // Consider only the last line of the pat string. - let extra_offset = match result.rfind('\n') { - // 1 for newline character - Some(idx) => result.len() - idx - 1 - offset, - None => result.len() - }; + let extra_offset = extra_offset(&result, offset); // The expression may (partionally) fit on the current line. if width > extra_offset + 1 { @@ -391,16 +390,19 @@ fn rewrite_call(context: &RewriteContext, 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)); + // 2 is for parens + let max_callee_width = try_opt!(width.checked_sub(2)); + let callee_str = try_opt!(callee.rewrite(context, max_callee_width, offset)); debug!("rewrite_call, callee_str: `{}`", callee_str); if args.len() == 0 { return Some(format!("{}()", callee_str)); } + let extra_offset = extra_offset(&callee_str, offset); // 2 is for parens. - let remaining_width = try_opt!(width.checked_sub(callee_str.len() + 2)); - let offset = callee_str.len() + 1 + offset; + let remaining_width = try_opt!(width.checked_sub(extra_offset + 2)); + let offset = offset + extra_offset + 1; let block_indent = expr_block_indent(context, offset); let inner_context = &RewriteContext { block_indent: block_indent, ..*context }; @@ -425,7 +427,7 @@ fn rewrite_call(context: &RewriteContext, indent: offset, h_width: remaining_width, v_width: remaining_width, - ends_with_newline: true, + ends_with_newline: false, }; Some(format!("{}({})", callee_str, write_list(&items, &fmt))) @@ -468,7 +470,9 @@ enum StructLitField<'a> { Base(&'a ast::Expr), } - let path_str = pprust::path_to_string(path); + // 2 = " {".len() + let path_str = try_opt!(path.rewrite(context, width - 2, offset)); + // Foo { a: Foo } - indent is +3, width is -5. let h_budget = width.checked_sub(path_str.len() + 5).unwrap_or(0); let (indent, v_budget) = match context.config.struct_lit_style { @@ -537,7 +541,7 @@ enum StructLitField<'a> { indent: indent, h_width: h_budget, v_width: v_budget, - ends_with_newline: true, + ends_with_newline: false, }; let fields_str = write_list(&items, &fmt); @@ -601,7 +605,7 @@ fn rewrite_tuple_lit(context: &RewriteContext, indent: indent, h_width: width - 2, v_width: width - 2, - ends_with_newline: true, + ends_with_newline: false, }; Some(format!("({})", write_list(&items, &fmt))) diff --git a/src/imports.rs b/src/imports.rs index 958dfadff66..e3629410c6c 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -11,11 +11,9 @@ use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic, ListTactic}; use utils::span_after; use rewrite::{Rewrite, RewriteContext}; -use config::Config; use syntax::ast; -use syntax::print::pprust; -use syntax::codemap::{CodeMap, Span}; +use syntax::codemap::Span; // TODO (some day) remove unused imports, expand globs, compress many single imports into a list import @@ -29,20 +27,21 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti path, path_list, self.span, - context.codemap, - context.config).unwrap_or("".to_owned())) + context).unwrap_or("".to_owned())) } ast::ViewPath_::ViewPathGlob(_) => { // FIXME convert to list? None } ast::ViewPath_::ViewPathSimple(ident, ref path) => { - let path_str = pprust::path_to_string(path); + let ident_str = ident.to_string(); + // 4 = " as ".len() + let path_str = try_opt!(path.rewrite(context, width - ident_str.len() - 4, offset)); Some(if path.segments.last().unwrap().identifier == ident { path_str } else { - format!("{} as {}", path_str, ident) + format!("{} as {}", path_str, ident_str) }) } } @@ -74,10 +73,10 @@ pub fn rewrite_use_list(width: usize, path: &ast::Path, path_list: &[ast::PathListItem], span: Span, - codemap: &CodeMap, - config: &Config) + context: &RewriteContext) -> Option { - let path_str = pprust::path_to_string(path); + // 1 = {} + let path_str = try_opt!(path.rewrite(context, width - 1, offset)); match path_list.len() { 0 => return None, @@ -106,10 +105,10 @@ pub fn rewrite_use_list(width: usize, // available // (loose 1 column (";")) v_width: remaining_width, - ends_with_newline: true, + ends_with_newline: false, }; - let mut items = itemize_list(codemap, + let mut items = itemize_list(context.codemap, vec![ListItem::from_str("")], /* Dummy value, explanation * below */ path_list.iter(), @@ -125,7 +124,7 @@ pub fn rewrite_use_list(width: usize, "self".to_owned() } }, - span_after(span, "{", codemap), + span_after(span, "{", context.codemap), span.hi); // We prefixed the item list with a dummy value so that we can @@ -140,7 +139,7 @@ pub fn rewrite_use_list(width: usize, 1 }; - if config.reorder_imports { + if context.config.reorder_imports { items[1..].sort_by(|a, b| a.item.cmp(&b.item)); } diff --git a/src/items.rs b/src/items.rs index 55b55918131..bbe80bf1231 100644 --- a/src/items.rs +++ b/src/items.rs @@ -16,6 +16,8 @@ use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic, ListTactic}; use comment::FindUncommented; use visitor::FmtVisitor; +use rewrite::Rewrite; +use config::Config; use syntax::{ast, abi}; use syntax::codemap::{self, Span, BytePos}; @@ -215,6 +217,7 @@ fn rewrite_fn_base(&mut self, // Where clause. result.push_str(&self.rewrite_where_clause(where_clause, + self.config, indent, span.hi)); @@ -281,7 +284,7 @@ fn rewrite_args(&self, indent: arg_indent, h_width: one_line_budget, v_width: multi_line_budget, - ends_with_newline: true, + ends_with_newline: false, }; write_list(&arg_items, &fmt) @@ -429,7 +432,7 @@ fn visit_variant(&mut self, field: &ast::Variant, last_field: bool, next_span_st indent: indent, h_width: budget, v_width: budget, - ends_with_newline: false, + ends_with_newline: true, }; result.push_str(&write_list(&items, &fmt)); result.push(')'); @@ -557,7 +560,7 @@ fn format_struct(&self, indent: offset + self.config.tab_spaces, h_width: self.config.max_width, v_width: budget, - ends_with_newline: false, + ends_with_newline: true, }; result.push_str(&write_list(&items, &fmt)); @@ -608,6 +611,7 @@ fn format_generics(&self, if generics.where_clause.predicates.len() > 0 || result.contains('\n') { result.push_str(&self.rewrite_where_clause(&generics.where_clause, + self.config, self.block_indent, span.hi)); result.push_str(&make_indent(self.block_indent)); @@ -664,8 +668,15 @@ fn rewrite_generics(&self, generics: &ast::Generics, offset: usize, span: Span) result.push('<'); // Strings for the generics. - let lt_strs = lifetimes.iter().map(|l| self.rewrite_lifetime_def(l)); - let ty_strs = tys.iter().map(|ty| self.rewrite_ty_param(ty)); + // 1 = < + let context = self.get_context(); + // FIXME: don't unwrap + let lt_strs = lifetimes.iter().map(|lt| { + lt.rewrite(&context, budget, offset + 1).unwrap() + }); + let ty_strs = tys.iter().map(|ty_param| { + ty_param.rewrite(&context, budget, offset + 1).unwrap() + }); // Extract comments between generics. let lt_spans = lifetimes.iter().map(|l| { @@ -700,7 +711,7 @@ fn rewrite_generics(&self, generics: &ast::Generics, offset: usize, span: Span) indent: offset + 1, h_width: budget, v_width: budget, - ends_with_newline: true, + ends_with_newline: false, }; result.push_str(&write_list(&items, &fmt)); @@ -711,6 +722,7 @@ fn rewrite_generics(&self, generics: &ast::Generics, offset: usize, span: Span) fn rewrite_where_clause(&self, where_clause: &ast::WhereClause, + config: &Config, indent: usize, span_end: BytePos) -> String { @@ -720,9 +732,13 @@ fn rewrite_where_clause(&self, } result.push('\n'); - result.push_str(&make_indent(indent + 4)); + result.push_str(&make_indent(indent + config.tab_spaces)); result.push_str("where "); + let context = self.get_context(); + // 6 = "where ".len() + let offset = indent + config.tab_spaces + 6; + let budget = self.config.ideal_width + self.config.leeway - offset; let span_start = span_for_where_pred(&where_clause.predicates[0]).lo; let items = itemize_list(self.codemap, Vec::new(), @@ -731,19 +747,21 @@ fn rewrite_where_clause(&self, "{", |pred| span_for_where_pred(pred).lo, |pred| span_for_where_pred(pred).hi, - |pred| self.rewrite_pred(pred), + // FIXME: we should handle failure better + // this will be taken care of when write_list + // takes Rewrite object: see issue #133 + |pred| pred.rewrite(&context, budget, offset).unwrap(), span_start, span_end); - let budget = self.config.ideal_width + self.config.leeway - indent - 10; let fmt = ListFormatting { tactic: ListTactic::Vertical, separator: ",", trailing_separator: SeparatorTactic::Never, - indent: indent + 10, + indent: offset, h_width: budget, v_width: budget, - ends_with_newline: true, + ends_with_newline: false, }; result.push_str(&write_list(&items, &fmt)); diff --git a/src/lists.rs b/src/lists.rs index 13c0cf763e3..6398c19c9ea 100644 --- a/src/lists.rs +++ b/src/lists.rs @@ -211,7 +211,7 @@ pub fn write_list<'b>(items: &[ListItem], formatting: &ListFormatting<'b>) -> St 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 || + 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); diff --git a/src/types.rs b/src/types.rs index c2e27c31684..4917d6ac3c9 100644 --- a/src/types.rs +++ b/src/types.rs @@ -8,33 +8,311 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use visitor::FmtVisitor; +use std::fmt; use syntax::ast; use syntax::print::pprust; +use syntax::codemap::{self, Span, BytePos, CodeMap}; -impl<'a> FmtVisitor<'a> { - pub fn rewrite_pred(&self, predicate: &ast::WherePredicate) -> String { - // TODO dead spans +use lists::{itemize_list, write_list, ListTactic, SeparatorTactic, ListFormatting}; +use rewrite::{Rewrite, RewriteContext}; +use utils::{extra_offset, span_after}; + +impl Rewrite for ast::Path { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { + rewrite_path(context, None, self, width, offset) + } +} + +// Does not wrap on simple segments. +pub fn rewrite_path(context: &RewriteContext, + qself: Option<&ast::QSelf>, + path: &ast::Path, + width: usize, + offset: usize) + -> Option { + let skip_count = qself.map(|x| x.position).unwrap_or(0); + + let mut result = if path.global { + "::".to_owned() + } else { + String::new() + }; + + let mut span_lo = path.span.lo; + + if let Some(ref qself) = qself { + result.push('<'); + result.push_str(&pprust::ty_to_string(&qself.ty)); + result.push_str(" as "); + + let extra_offset = extra_offset(&result, offset); + // 3 = ">::".len() + let budget = try_opt!(width.checked_sub(extra_offset)) - 3; + + result = try_opt!(rewrite_path_segments(result, + path.segments.iter().take(skip_count), + span_lo, + path.span.hi, + context, + budget, + offset + extra_offset)); + + result.push_str(">::"); + span_lo = qself.ty.span.hi + BytePos(1); + } + + let extra_offset = extra_offset(&result, offset); + let budget = try_opt!(width.checked_sub(extra_offset)); + rewrite_path_segments(result, + path.segments.iter().skip(skip_count), + span_lo, + path.span.hi, + context, + budget, + offset + extra_offset) +} + +fn rewrite_path_segments<'a, I>(mut buffer: String, + iter: I, + mut span_lo: BytePos, + span_hi: BytePos, + context: &RewriteContext, + width: usize, + offset: usize) + -> Option + where I: Iterator +{ + let mut first = true; + + for segment in iter { + let extra_offset = extra_offset(&buffer, offset); + let remaining_width = try_opt!(width.checked_sub(extra_offset)); + let new_offset = offset + extra_offset; + let segment_string = try_opt!(rewrite_segment(segment, + &mut span_lo, + span_hi, + context, + remaining_width, + new_offset)); + + if first { + first = false; + } else { + buffer.push_str("::"); + } + + buffer.push_str(&segment_string); + } + + Some(buffer) +} + +enum SegmentParam<'a> { + LifeTime(&'a ast::Lifetime), + Type(&'a ast::Ty), + Binding(&'a ast::TypeBinding), +} + +impl<'a> SegmentParam<'a> { + fn get_span(&self) -> Span { + match *self { + SegmentParam::LifeTime(ref lt) => lt.span, + SegmentParam::Type(ref ty) => ty.span, + SegmentParam::Binding(ref binding) => binding.span, + } + } +} + +impl<'a> fmt::Display for SegmentParam<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + SegmentParam::LifeTime(ref lt) => { + write!(f, "{}", pprust::lifetime_to_string(lt)) + } + SegmentParam::Type(ref ty) => { + write!(f, "{}", pprust::ty_to_string(ty)) + } + SegmentParam::Binding(ref binding) => { + write!(f, "{} = {}", binding.ident, pprust::ty_to_string(&*binding.ty)) + } + } + } +} + +// This is a dirty hack to determine if we're in an expression or not. Generic +// parameters are passed differently in expressions and items. We'd declare +// a struct with Foo, but call its functions with Foo::::f(). +// We'd really rather not do this, but there doesn't seem to be an alternative +// at this point. +// FIXME: fails with spans containing comments with the characters < or : +fn get_path_separator(codemap: &CodeMap, + path_start: BytePos, + segment_start: BytePos) + -> &'static str { + let span = codemap::mk_sp(path_start, segment_start); + let snippet = codemap.span_to_snippet(span).unwrap(); + + for c in snippet.chars().rev() { + if c == ':' { + return "::" + } else if c.is_whitespace() || c == '<' { + continue; + } else { + return ""; + } + } + + unreachable!(); +} + +// Formats a path segment. There are some hacks involved to correctly determine +// the segment's associated span since it's not part of the AST. +// +// The span_lo is assumed to be greater than the end of any previous segment's +// parameters and lesser or equal than the start of current segment. +// +// span_hi is assumed equal to the end of the entire path. +// +// When the segment contains a positive number of parameters, we update span_lo +// so that invariants described above will hold for the next segment. +fn rewrite_segment(segment: &ast::PathSegment, + span_lo: &mut BytePos, + span_hi: BytePos, + context: &RewriteContext, + width: usize, + offset: usize) + -> Option { + let ident_len = segment.identifier.to_string().len(); + let width = try_opt!(width.checked_sub(ident_len)); + let offset = offset + ident_len; + + let params = match segment.parameters { + ast::PathParameters::AngleBracketedParameters(ref data) if data.lifetimes.len() > 0 || + data.types.len() > 0 || + data.bindings.len() > 0 => { + let param_list = data.lifetimes.iter() + .map(SegmentParam::LifeTime) + .chain(data.types.iter() + .map(|x| SegmentParam::Type(&*x))) + .chain(data.bindings.iter() + .map(|x| SegmentParam::Binding(&*x))) + .collect::>(); + + let next_span_lo = param_list.last().unwrap().get_span().hi + BytePos(1); + let list_lo = span_after(codemap::mk_sp(*span_lo, span_hi), "<", context.codemap); + let separator = get_path_separator(context.codemap, *span_lo, list_lo); + + let items = itemize_list(context.codemap, + Vec::new(), + param_list.into_iter(), + ",", + ">", + |param| param.get_span().lo, + |param| param.get_span().hi, + ToString::to_string, + list_lo, + span_hi); + + // 1 for < + let extra_offset = 1 + separator.len(); + // 1 for > + let list_width = try_opt!(width.checked_sub(extra_offset + 1)); + + let fmt = ListFormatting { + tactic: ListTactic::HorizontalVertical, + separator: ",", + trailing_separator: SeparatorTactic::Never, + indent: offset + extra_offset, + h_width: list_width, + v_width: list_width, + ends_with_newline: false, + }; + + // update pos + *span_lo = next_span_lo; + + format!("{}<{}>", separator, write_list(&items, &fmt)) + } + ast::PathParameters::ParenthesizedParameters(ref data) => { + let output = match data.output { + Some(ref ty) => format!(" -> {}", pprust::ty_to_string(&*ty)), + None => String::new() + }; + + let list_lo = span_after(codemap::mk_sp(*span_lo, span_hi), "(", context.codemap); + let items = itemize_list(context.codemap, + Vec::new(), + data.inputs.iter(), + ",", + ")", + |ty| ty.span.lo, + |ty| ty.span.hi, + |ty| pprust::ty_to_string(ty), + list_lo, + span_hi); + + // 2 for () + let budget = try_opt!(width.checked_sub(output.len() + 2)); + + let fmt = ListFormatting { + tactic: ListTactic::HorizontalVertical, + separator: ",", + trailing_separator: SeparatorTactic::Never, + // 1 for ( + indent: offset + 1, + h_width: budget, + v_width: budget, + ends_with_newline: false, + }; + + // update pos + *span_lo = data.inputs.last().unwrap().span.hi + BytePos(1); + + format!("({}){}", write_list(&items, &fmt), output) + } + _ => String::new() + }; + + Some(format!("{}{}", segment.identifier, params)) +} + +impl Rewrite for ast::WherePredicate { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { + // TODO dead spans? // TODO assumes we'll always fit on one line... - match predicate { + Some(match self { &ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate{ref bound_lifetimes, ref bounded_ty, ref bounds, ..}) => { if bound_lifetimes.len() > 0 { - format!("for<{}> {}: {}", - bound_lifetimes.iter().map(|l| self.rewrite_lifetime_def(l)) - .collect::>().join(", "), - pprust::ty_to_string(bounded_ty), - bounds.iter().map(|b| self.rewrite_ty_bound(b)) - .collect::>().join(" + ")) + let lifetime_str = bound_lifetimes.iter().map(|lt| { + lt.rewrite(context, width, offset).unwrap() + }).collect::>().join(", "); + let type_str = pprust::ty_to_string(bounded_ty); + // 8 = "for<> : ".len() + let used_width = lifetime_str.len() + type_str.len() + 8; + let bounds_str = bounds.iter().map(|ty_bound| { + ty_bound.rewrite(context, + width - used_width, + offset + used_width) + .unwrap() + }).collect::>().join(" + "); + format!("for<{}> {}: {}", lifetime_str, type_str, bounds_str) } else { - format!("{}: {}", - pprust::ty_to_string(bounded_ty), - bounds.iter().map(|b| self.rewrite_ty_bound(b)) - .collect::>().join(" + ")) + let type_str = pprust::ty_to_string(bounded_ty); + // 2 = ": ".len() + let used_width = type_str.len() + 2; + let bounds_str = bounds.iter().map(|ty_bound| { + ty_bound.rewrite(context, + width - used_width, + offset + used_width) + .unwrap() + }).collect::>().join(" + "); + + format!("{}: {}", type_str, bounds_str) } } &ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate{ref lifetime, @@ -42,65 +320,91 @@ pub fn rewrite_pred(&self, predicate: &ast::WherePredicate) -> String { ..}) => { format!("{}: {}", pprust::lifetime_to_string(lifetime), - bounds.iter().map(|l| pprust::lifetime_to_string(l)) - .collect::>().join(" + ")) + bounds.iter().map(pprust::lifetime_to_string) + .collect::>().join(" + ")) } &ast::WherePredicate::EqPredicate(ast::WhereEqPredicate{ref path, ref ty, ..}) => { - format!("{} = {}", pprust::path_to_string(path), pprust::ty_to_string(ty)) + let ty_str = pprust::ty_to_string(ty); + // 3 = " = ".len() + let used_width = 3 + ty_str.len(); + let path_str = try_opt!(path.rewrite(context, + width - used_width, + offset + used_width)); + format!("{} = {}", path_str, ty_str) } + }) + } +} + +impl Rewrite for ast::LifetimeDef { + fn rewrite(&self, _: &RewriteContext, _: usize, _: usize) -> Option { + if self.bounds.len() == 0 { + Some(pprust::lifetime_to_string(&self.lifetime)) + } else { + Some(format!("{}: {}", + pprust::lifetime_to_string(&self.lifetime), + self.bounds.iter().map(pprust::lifetime_to_string) + .collect::>().join(" + "))) } } +} - pub fn rewrite_lifetime_def(&self, lifetime: &ast::LifetimeDef) -> String { - if lifetime.bounds.len() == 0 { - return pprust::lifetime_to_string(&lifetime.lifetime); - } - - format!("{}: {}", - pprust::lifetime_to_string(&lifetime.lifetime), - lifetime.bounds.iter().map(|l| pprust::lifetime_to_string(l)) - .collect::>().join(" + ")) - } - - pub fn rewrite_ty_bound(&self, bound: &ast::TyParamBound) -> String { - match *bound { +impl Rewrite for ast::TyParamBound { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { + match *self { ast::TyParamBound::TraitTyParamBound(ref tref, ast::TraitBoundModifier::None) => { - self.rewrite_poly_trait_ref(tref) + tref.rewrite(context, width, offset) } ast::TyParamBound::TraitTyParamBound(ref tref, ast::TraitBoundModifier::Maybe) => { - format!("?{}", self.rewrite_poly_trait_ref(tref)) + Some(format!("?{}", try_opt!(tref.rewrite(context, width - 1, offset + 1)))) } ast::TyParamBound::RegionTyParamBound(ref l) => { - pprust::lifetime_to_string(l) + Some(pprust::lifetime_to_string(l)) } } } +} - pub fn rewrite_ty_param(&self, ty_param: &ast::TyParam) -> String { +// FIXME: this assumes everything will fit on one line +impl Rewrite for ast::TyParam { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { let mut result = String::with_capacity(128); - result.push_str(&ty_param.ident.to_string()); - if ty_param.bounds.len() > 0 { + result.push_str(&self.ident.to_string()); + if self.bounds.len() > 0 { result.push_str(": "); - result.push_str(&ty_param.bounds.iter().map(|b| self.rewrite_ty_bound(b)) - .collect::>().join(" + ")); + + let bounds = self.bounds.iter().map(|ty_bound| { + ty_bound.rewrite(context, width, offset).unwrap() + }).collect::>().join(" + "); + + result.push_str(&bounds); } - if let Some(ref def) = ty_param.default { + if let Some(ref def) = self.default { result.push_str(" = "); result.push_str(&pprust::ty_to_string(&def)); } - result + Some(result) } +} - fn rewrite_poly_trait_ref(&self, t: &ast::PolyTraitRef) -> String { - if t.bound_lifetimes.len() > 0 { - format!("for<{}> {}", - t.bound_lifetimes.iter().map(|l| self.rewrite_lifetime_def(l)) - .collect::>().join(", "), - pprust::path_to_string(&t.trait_ref.path)) +// FIXME: this assumes everything will fit on one line +impl Rewrite for ast::PolyTraitRef { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { + if self.bound_lifetimes.len() > 0 { + let lifetime_str = self.bound_lifetimes.iter().map(|lt| { + lt.rewrite(context, width, offset).unwrap() + }).collect::>().join(", "); + // 6 is "for<> ".len() + let extra_offset = lifetime_str.len() + 6; + let max_path_width = try_opt!(width.checked_sub(extra_offset)); + let path_str = try_opt!(self.trait_ref.path.rewrite(context, + max_path_width, + offset + extra_offset)); + Some(format!("for<{}> {}", lifetime_str, path_str)) } else { - pprust::path_to_string(&t.trait_ref.path) + self.trait_ref.path.rewrite(context, width, offset) } } } diff --git a/src/utils.rs b/src/utils.rs index 3ff30da9784..59f85b3ad27 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -15,6 +15,16 @@ use SKIP_ANNOTATION; +// Computes the length of a string's last line, minus offset. +#[inline] +pub fn extra_offset(text: &str, offset: usize) -> usize { + match text.rfind('\n') { + // 1 for newline character + Some(idx) => text.len() - idx - 1 - offset, + None => text.len() + } +} + #[inline] pub fn span_after(original: Span, needle: &str, codemap: &CodeMap) -> BytePos { let snippet = codemap.span_to_snippet(original).unwrap(); diff --git a/src/visitor.rs b/src/visitor.rs index 23908edf8d0..e107a32abf8 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -33,13 +33,9 @@ fn visit_expr(&mut self, ex: &'v ast::Expr) { self.codemap.lookup_char_pos(ex.span.lo), self.codemap.lookup_char_pos(ex.span.hi)); self.format_missing(ex.span.lo); + let offset = self.buffer.cur_offset(); - let context = RewriteContext { - codemap: self.codemap, - config: self.config, - block_indent: self.block_indent, - }; - let rewrite = ex.rewrite(&context, self.config.max_width - offset, offset); + let rewrite = ex.rewrite(&self.get_context(), self.config.max_width - offset, offset); if let Some(new_str) = rewrite { self.buffer.push_str(&new_str); @@ -372,4 +368,12 @@ fn format_import(&mut self, vis: ast::Visibility, vp: &ast::ViewPath, span: Span } } } + + pub fn get_context(&self) -> RewriteContext { + RewriteContext { + codemap: self.codemap, + config: self.config, + block_indent: self.block_indent, + } + } } diff --git a/tests/source/fn-simple.rs b/tests/source/fn-simple.rs index 9bcf3166722..b5682d36cfa 100644 --- a/tests/source/fn-simple.rs +++ b/tests/source/fn-simple.rs @@ -9,3 +9,11 @@ fn weird_comment(/* /*/ double level */ comment */ x: Hello /*/*/* tripple, even ) { simple(/* does this preserve comments now? */ 42, NoWay) } + +fn generic(arg: T) -> &SomeType + where T: Fn(// First arg + A, + // Second argument + B, C, D, /* pre comment */ E /* last comment */) -> &SomeType { + arg(a, b, c, d, e) +} diff --git a/tests/source/paths.rs b/tests/source/paths.rs new file mode 100644 index 00000000000..4225aa9dab3 --- /dev/null +++ b/tests/source/paths.rs @@ -0,0 +1,22 @@ + +fn main() { + // FIXME(#133): the list rewrite should fail and force a different format + let constellation_chan = Constellation:: ::start( + compositor_proxy, + resource_task, + image_cache_task,font_cache_task, + time_profiler_chan, + mem_profiler_chan, + devtools_chan, + storage_task, + supports_clipboard + ); + + Quux::::some_func(); + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA::BBBBBBBBBBBBBBBBBBBBBBBBBBBB::CCCCCCCCCCCCCCCCCCCCCC::quux(); +} + +fn op(foo: Bar, key : &[u8], upd : Fn(Option<&memcache::Item> , Baz ) -> Result) -> MapResult {} diff --git a/tests/source/struct_lits.rs b/tests/source/struct_lits.rs index 54f2ea5441b..225d3a16985 100644 --- a/tests/source/struct_lits.rs +++ b/tests/source/struct_lits.rs @@ -48,3 +48,8 @@ fn matcher() { }, }; } + +fn issue177() { + struct Foo { memb: T } + let foo = Foo:: { memb: 10 }; +} diff --git a/tests/target/fn-simple.rs b/tests/target/fn-simple.rs index eb133d568ed..dbb4f82924a 100644 --- a/tests/target/fn-simple.rs +++ b/tests/target/fn-simple.rs @@ -14,3 +14,16 @@ fn weird_comment(// /*/ double level */ comment 42, NoWay) } + +fn generic(arg: T) -> &SomeType + where T: Fn(// First arg + A, + // Second argument + B, + C, + D, + // pre comment + E /* last comment */) -> &SomeType +{ + arg(a, b, c, d, e) +} diff --git a/tests/target/fn.rs b/tests/target/fn.rs index 6c41f62a8b2..bc05efa5351 100644 --- a/tests/target/fn.rs +++ b/tests/target/fn.rs @@ -79,17 +79,3 @@ fn main() { let _ = function(move || 5); let _ = move || 42; } - -fn servo() { - let constellation_chan = Constellation::::start( - compositor_proxy, - resource_task, - image_cache_task, - font_cache_task, - time_profiler_chan, - mem_profiler_chan, - devtools_chan, - storage_task, - supports_clipboard); -} diff --git a/tests/target/paths.rs b/tests/target/paths.rs new file mode 100644 index 00000000000..c358104e1fa --- /dev/null +++ b/tests/target/paths.rs @@ -0,0 +1,21 @@ + +fn main() { + // FIXME(#133): the list rewrite should fail and force a different format + let constellation_chan = Constellation::::start(compositor_proxy, + resource_task, + image_cache_task, + font_cache_task, + time_profiler_chan, + mem_profiler_chan, + devtools_chan, + storage_task, + supports_clipboard); + + Quux::::some_func(); + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA::BBBBBBBBBBBBBBBBBBBBBBBBBBBB::CCCCCCCCCCCCCCCCCCCCCC::quux(); +} + +fn op(foo: Bar, key: &[u8], upd: Fn(Option<&memcache::Item>, Baz) -> Result) -> MapResult { +} diff --git a/tests/target/struct_lits.rs b/tests/target/struct_lits.rs index 9f7ab5cb428..cc2887fc51f 100644 --- a/tests/target/struct_lits.rs +++ b/tests/target/struct_lits.rs @@ -64,3 +64,10 @@ fn matcher() { }, }; } + +fn issue177() { + struct Foo { + memb: T, + } + let foo = Foo:: { memb: 10 }; +}