about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorNick Cameron <ncameron@mozilla.com>2017-01-31 08:28:48 +1300
committerNick Cameron <ncameron@mozilla.com>2017-01-31 08:35:23 +1300
commit428339fdc3c572b0b4a4e86ce1fc2ab048cccac0 (patch)
treea6f024f772b19db996ada08783190c50aced0b5f /src
parent6054f28bd2c2b617d4d494c5e27e09b4b16db1d2 (diff)
Refactor indent and width into Shape struct
Diffstat (limited to 'src')
-rw-r--r--src/chains.rs84
-rw-r--r--src/comment.rs40
-rw-r--r--src/expr.rs663
-rw-r--r--src/imports.rs31
-rw-r--r--src/items.rs302
-rw-r--r--src/lib.rs44
-rw-r--r--src/lists.rs62
-rw-r--r--src/macros.rs19
-rw-r--r--src/missed_spans.rs5
-rw-r--r--src/patterns.rs123
-rw-r--r--src/rewrite.rs12
-rw-r--r--src/string.rs14
-rw-r--r--src/types.rs307
-rw-r--r--src/utils.rs16
-rw-r--r--src/visitor.rs30
15 files changed, 866 insertions, 886 deletions
diff --git a/src/chains.rs b/src/chains.rs
index 1220d43356e..38f6fad1166 100644
--- a/src/chains.rs
+++ b/src/chains.rs
@@ -81,7 +81,7 @@
 /// true, then we allow the last method call to spill over multiple lines without
 /// forcing the rest of the chain to be split.
 
-use Indent;
+use {Indent, Shape};
 use rewrite::{Rewrite, RewriteContext};
 use utils::{wrap_str, first_line_width};
 use expr::rewrite_call;
@@ -92,24 +92,20 @@ use std::iter;
 use syntax::{ast, ptr};
 use syntax::codemap::{mk_sp, Span};
 
-pub fn rewrite_chain(expr: &ast::Expr,
-                     context: &RewriteContext,
-                     width: usize,
-                     offset: Indent)
-                     -> Option<String> {
+pub fn rewrite_chain(expr: &ast::Expr, context: &RewriteContext, shape: Shape) -> Option<String> {
     let total_span = expr.span;
     let (parent, subexpr_list) = make_subexpr_list(expr, context);
 
     // Bail out if the chain is just try sugar, i.e., an expression followed by
     // any number of `?`s.
     if chain_only_try(&subexpr_list) {
-        return rewrite_try(&parent, subexpr_list.len(), context, width, offset);
+        return rewrite_try(&parent, subexpr_list.len(), context, shape);
     }
 
     // Parent is the first item in the chain, e.g., `foo` in `foo.bar.baz()`.
-    let parent_block_indent = chain_base_indent(context, offset);
+    let parent_block_indent = chain_base_indent(context, shape.indent);
     let parent_context = &RewriteContext { block_indent: parent_block_indent, ..*context };
-    let parent_rewrite = try_opt!(parent.rewrite(parent_context, width, offset));
+    let parent_rewrite = try_opt!(parent.rewrite(parent_context, shape));
 
     // Decide how to layout the rest of the chain. `extend` is true if we can
     // put the first non-parent item on the same line as the parent.
@@ -119,7 +115,7 @@ pub fn rewrite_chain(expr: &ast::Expr,
         let indent = if let ast::ExprKind::Try(..) = subexpr_list.last().unwrap().node {
             parent_block_indent.block_indent(context.config)
         } else {
-            chain_indent(context, offset + Indent::new(0, parent_rewrite.len()))
+            chain_indent(context, shape.indent + Indent::new(0, parent_rewrite.len()))
         };
         (indent, true)
     } else if is_block_expr(&parent, &parent_rewrite) {
@@ -129,13 +125,13 @@ pub fn rewrite_chain(expr: &ast::Expr,
     } else if parent_rewrite.contains('\n') {
         (chain_indent(context, parent_block_indent.block_indent(context.config)), false)
     } else {
-        (chain_indent_newline(context, offset + Indent::new(0, parent_rewrite.len())), false)
+        (chain_indent_newline(context, shape.indent + Indent::new(0, parent_rewrite.len())), false)
     };
 
-    let max_width = try_opt!((width + offset.width()).checked_sub(indent.width()));
+    let max_width = try_opt!((shape.width + shape.indent.width()).checked_sub(indent.width()));
     let mut rewrites = try_opt!(subexpr_list.iter()
         .rev()
-        .map(|e| rewrite_chain_subexpr(e, total_span, context, max_width, indent))
+        .map(|e| rewrite_chain_subexpr(e, total_span, context, Shape::legacy(max_width, indent)))
         .collect::<Option<Vec<_>>>());
 
     // Total of all items excluding the last.
@@ -156,7 +152,7 @@ pub fn rewrite_chain(expr: &ast::Expr,
         false
     };
 
-    let mut fits_single_line = !veto_single_line && total_width <= width;
+    let mut fits_single_line = !veto_single_line && total_width <= shape.width;
     if fits_single_line {
         let len = rewrites.len();
         let (init, last) = rewrites.split_at_mut(len - 1);
@@ -168,10 +164,9 @@ pub fn rewrite_chain(expr: &ast::Expr,
                     rewrite_method_call_with_overflow(e,
                                                       &mut last[0],
                                                       almost_total,
-                                                      width,
                                                       total_span,
                                                       context,
-                                                      offset)
+                                                      shape)
                 }
                 _ => !last[0].contains('\n'),
             }
@@ -199,8 +194,7 @@ pub fn rewrite_chain(expr: &ast::Expr,
                      first_connector,
                      join_rewrites(&rewrites, &subexpr_list, &connector)),
              context.config.max_width,
-             width,
-             offset)
+             shape)
 }
 
 // True if the chain is only `?`s.
@@ -215,10 +209,9 @@ fn chain_only_try(exprs: &[ast::Expr]) -> bool {
 pub fn rewrite_try(expr: &ast::Expr,
                    try_count: usize,
                    context: &RewriteContext,
-                   width: usize,
-                   offset: Indent)
+                   shape: Shape)
                    -> Option<String> {
-    let sub_expr = try_opt!(expr.rewrite(context, width - try_count, offset));
+    let sub_expr = try_opt!(expr.rewrite(context, shape.sub_width(try_count)));
     Some(format!("{}{}",
                  sub_expr,
                  iter::repeat("?").take(try_count).collect::<String>()))
@@ -305,13 +298,12 @@ fn chain_indent_newline(context: &RewriteContext, _offset: Indent) -> Indent {
 fn rewrite_method_call_with_overflow(expr_kind: &ast::ExprKind,
                                      last: &mut String,
                                      almost_total: usize,
-                                     width: usize,
                                      total_span: Span,
                                      context: &RewriteContext,
-                                     offset: Indent)
+                                     shape: Shape)
                                      -> bool {
     if let &ast::ExprKind::MethodCall(ref method_name, ref types, ref expressions) = expr_kind {
-        let budget = match width.checked_sub(almost_total) {
+        let budget = match shape.width.checked_sub(almost_total) {
             Some(b) => b,
             None => return false,
         };
@@ -320,8 +312,8 @@ fn rewrite_method_call_with_overflow(expr_kind: &ast::ExprKind,
                                                    expressions,
                                                    total_span,
                                                    context,
-                                                   budget,
-                                                   offset + almost_total);
+                                                   Shape::legacy(budget,
+                                                                 shape.indent + almost_total));
 
         if let Some(ref mut s) = last_rewrite {
             ::std::mem::swap(s, last);
@@ -366,29 +358,36 @@ fn convert_try(expr: &ast::Expr, context: &RewriteContext) -> ast::Expr {
 fn rewrite_chain_subexpr(expr: &ast::Expr,
                          span: Span,
                          context: &RewriteContext,
-                         width: usize,
-                         offset: Indent)
+                         shape: Shape)
                          -> Option<String> {
     match expr.node {
         ast::ExprKind::MethodCall(ref method_name, ref types, ref expressions) => {
-            let inner = &RewriteContext { block_indent: offset, ..*context };
-            rewrite_method_call(method_name.node,
-                                types,
-                                expressions,
-                                span,
-                                inner,
-                                width,
-                                offset)
+            let inner = &RewriteContext { block_indent: shape.indent, ..*context };
+            rewrite_method_call(method_name.node, types, expressions, span, inner, shape)
         }
         ast::ExprKind::Field(_, ref field) => {
             let s = format!(".{}", field.node);
-            if s.len() <= width { Some(s) } else { None }
+            if s.len() <= shape.width {
+                Some(s)
+            } else {
+                None
+            }
         }
         ast::ExprKind::TupField(_, ref field) => {
             let s = format!(".{}", field.node);
-            if s.len() <= width { Some(s) } else { None }
+            if s.len() <= shape.width {
+                Some(s)
+            } else {
+                None
+            }
+        }
+        ast::ExprKind::Try(_) => {
+            if shape.width >= 1 {
+                Some("?".into())
+            } else {
+                None
+            }
         }
-        ast::ExprKind::Try(_) => if width >= 1 { Some("?".into()) } else { None },
         _ => unreachable!(),
     }
 }
@@ -406,14 +405,13 @@ fn rewrite_method_call(method_name: ast::Ident,
                        args: &[ptr::P<ast::Expr>],
                        span: Span,
                        context: &RewriteContext,
-                       width: usize,
-                       offset: Indent)
+                       shape: Shape)
                        -> Option<String> {
     let (lo, type_str) = if types.is_empty() {
         (args[0].span.hi, String::new())
     } else {
         let type_list: Vec<_> = try_opt!(types.iter()
-            .map(|ty| ty.rewrite(context, width, offset))
+            .map(|ty| ty.rewrite(context, shape))
             .collect());
 
         let type_str = if context.config.spaces_within_angle_brackets && type_list.len() > 0 {
@@ -428,5 +426,5 @@ fn rewrite_method_call(method_name: ast::Ident,
     let callee_str = format!(".{}{}", method_name, type_str);
     let span = mk_sp(lo, span.hi);
 
-    rewrite_call(context, &callee_str, &args[1..], span, width, offset)
+    rewrite_call(context, &callee_str, &args[1..], span, shape)
 }
diff --git a/src/comment.rs b/src/comment.rs
index 8eaf2b2fb4f..b7549f10c2d 100644
--- a/src/comment.rs
+++ b/src/comment.rs
@@ -14,7 +14,7 @@ use std::{self, iter};
 
 use syntax::codemap::Span;
 
-use Indent;
+use {Indent, Shape};
 use config::Config;
 use rewrite::RewriteContext;
 use string::{StringFormat, rewrite_string};
@@ -34,8 +34,7 @@ fn is_custom_comment(comment: &str) -> bool {
 
 pub fn rewrite_comment(orig: &str,
                        block_style: bool,
-                       width: usize,
-                       offset: Indent,
+                       shape: Shape,
                        config: &Config)
                        -> Option<String> {
     // If there are lines without a starting sigil, we won't format them correctly
@@ -50,7 +49,7 @@ pub fn rewrite_comment(orig: &str,
     }
 
     if !config.normalize_comments && !config.wrap_comments {
-        return light_rewrite_comment(orig, offset, config);
+        return light_rewrite_comment(orig, shape.indent, config);
     }
 
     let (opener, closer, line_start) =
@@ -85,15 +84,14 @@ pub fn rewrite_comment(orig: &str,
             ("// ", "", "// ")
         };
 
-    let max_chars = width.checked_sub(closer.len() + opener.len()).unwrap_or(1);
-    let indent_str = offset.to_string(config);
+    let max_chars = shape.width.checked_sub(closer.len() + opener.len()).unwrap_or(1);
+    let indent_str = shape.indent.to_string(config);
     let fmt = StringFormat {
         opener: "",
         closer: "",
         line_start: line_start,
         line_end: "",
-        width: max_chars,
-        offset: offset + (opener.len() - line_start.len()),
+        shape: Shape::legacy(max_chars, shape.indent + (opener.len() - line_start.len())),
         trim_end: true,
         config: config,
     };
@@ -574,14 +572,13 @@ impl<'a> Iterator for CommentCodeSlices<'a> {
 pub fn recover_comment_removed(new: String,
                                span: Span,
                                context: &RewriteContext,
-                               width: usize,
-                               offset: Indent)
+                               shape: Shape)
                                -> Option<String> {
     let snippet = context.snippet(span);
     if changed_comment_content(&snippet, &new) {
         // We missed some comments
         // Keep previous formatting if it satisfies the constrains
-        wrap_str(snippet, context.config.max_width, width, offset)
+        wrap_str(snippet, context.config.max_width, shape)
     } else {
         Some(new)
     }
@@ -678,7 +675,7 @@ fn remove_comment_header(comment: &str) -> &str {
 mod test {
     use super::{CharClasses, CodeCharKind, FullCodeCharKind, contains_comment, rewrite_comment,
                 FindUncommented, CommentCodeSlices};
-    use Indent;
+    use {Indent, Shape};
 
     #[test]
     fn char_classes() {
@@ -735,33 +732,36 @@ mod test {
         config.wrap_comments = true;
         config.normalize_comments = true;
 
-        let comment = rewrite_comment(" //test", true, 100, Indent::new(0, 100), &config).unwrap();
+        let comment = rewrite_comment(" //test",
+                                      true,
+                                      Shape::legacy(100, Indent::new(0, 100)),
+                                      &config).unwrap();
         assert_eq!("/* test */", comment);
 
         let comment = rewrite_comment("// comment on a",
                                       false,
-                                      10,
-                                      Indent::empty(),
+                                      Shape::legacy(10, Indent::empty()),
                                       &config).unwrap();
         assert_eq!("// comment\n// on a", comment);
 
         let comment = rewrite_comment("//  A multi line comment\n             // between args.",
                                       false,
-                                      60,
-                                      Indent::new(0, 12),
+                                      Shape::legacy(60, Indent::new(0, 12)),
                                       &config).unwrap();
         assert_eq!("//  A multi line comment\n            // between args.", comment);
 
         let input = "// comment";
         let expected =
             "/* comment */";
-        let comment = rewrite_comment(input, true, 9, Indent::new(0, 69), &config).unwrap();
+        let comment = rewrite_comment(input,
+                                      true,
+                                      Shape::legacy(9, Indent::new(0, 69)),
+                                      &config).unwrap();
         assert_eq!(expected, comment);
 
         let comment = rewrite_comment("/*   trimmed    */",
                                       true,
-                                      100,
-                                      Indent::new(0, 100),
+                                      Shape::legacy(100, Indent::new(0, 100)),
                                       &config).unwrap();
         assert_eq!("/* trimmed */", comment);
     }
diff --git a/src/expr.rs b/src/expr.rs
index 80937d76da7..ba7c05b6528 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -15,7 +15,7 @@ use std::ops::Deref;
 use std::iter::ExactSizeIterator;
 use std::fmt::Write;
 
-use {Indent, Spanned};
+use {Indent, Shape, Spanned};
 use codemap::SpanUtils;
 use rewrite::{Rewrite, RewriteContext};
 use lists::{write_list, itemize_list, ListFormatting, SeparatorTactic, ListTactic,
@@ -36,8 +36,8 @@ use syntax::codemap::{CodeMap, Span, BytePos, mk_sp};
 use syntax::parse::classify;
 
 impl Rewrite for ast::Expr {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
-        format_expr(self, ExprType::SubExpression, context, width, offset)
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
+        format_expr(self, ExprType::SubExpression, context, shape)
     }
 }
 
@@ -50,35 +50,28 @@ enum ExprType {
 fn format_expr(expr: &ast::Expr,
                expr_type: ExprType,
                context: &RewriteContext,
-               width: usize,
-               offset: Indent)
+               shape: Shape)
                -> Option<String> {
     let result = match expr.node {
         ast::ExprKind::Array(ref expr_vec) => {
             rewrite_array(expr_vec.iter().map(|e| &**e),
                           mk_sp(context.codemap.span_after(expr.span, "["), expr.span.hi),
                           context,
-                          width,
-                          offset)
+                          shape)
         }
         ast::ExprKind::Lit(ref l) => {
             match l.node {
                 ast::LitKind::Str(_, ast::StrStyle::Cooked) => {
-                    rewrite_string_lit(context, l.span, width, offset)
-                }
-                _ => {
-                    wrap_str(context.snippet(expr.span),
-                             context.config.max_width,
-                             width,
-                             offset)
+                    rewrite_string_lit(context, l.span, shape)
                 }
+                _ => wrap_str(context.snippet(expr.span), context.config.max_width, shape),
             }
         }
         ast::ExprKind::Call(ref callee, ref args) => {
             let inner_span = mk_sp(callee.span.hi, expr.span.hi);
-            rewrite_call(context, &**callee, args, inner_span, width, offset)
+            rewrite_call(context, &**callee, args, inner_span, shape)
         }
-        ast::ExprKind::Paren(ref subexpr) => rewrite_paren(context, subexpr, width, offset),
+        ast::ExprKind::Paren(ref subexpr) => rewrite_paren(context, subexpr, shape),
         ast::ExprKind::Binary(ref op, ref lhs, ref rhs) => {
             // FIXME: format comments between operands and operator
             rewrite_pair(&**lhs,
@@ -87,43 +80,33 @@ fn format_expr(expr: &ast::Expr,
                          &format!(" {} ", context.snippet(op.span)),
                          "",
                          context,
-                         width,
-                         offset)
-        }
-        ast::ExprKind::Unary(ref op, ref subexpr) => {
-            rewrite_unary_op(context, op, subexpr, width, offset)
+                         shape)
         }
+        ast::ExprKind::Unary(ref op, ref subexpr) => rewrite_unary_op(context, op, subexpr, shape),
         ast::ExprKind::Struct(ref path, ref fields, ref base) => {
             rewrite_struct_lit(context,
                                path,
                                fields,
                                base.as_ref().map(|e| &**e),
                                expr.span,
-                               width,
-                               offset)
+                               shape)
         }
         ast::ExprKind::Tup(ref items) => {
-            rewrite_tuple(context,
-                          items.iter().map(|x| &**x),
-                          expr.span,
-                          width,
-                          offset)
+            rewrite_tuple(context, items.iter().map(|x| &**x), expr.span, shape)
         }
         ast::ExprKind::While(ref cond, ref block, label) => {
-            ControlFlow::new_while(None, cond, block, label, expr.span)
-                .rewrite(context, width, offset)
+            ControlFlow::new_while(None, cond, block, label, expr.span).rewrite(context, shape)
         }
         ast::ExprKind::WhileLet(ref pat, ref cond, ref block, label) => {
-            ControlFlow::new_while(Some(pat), cond, block, label, expr.span)
-                .rewrite(context, width, offset)
+            ControlFlow::new_while(Some(pat), cond, block, label, expr.span).rewrite(context, shape)
         }
         ast::ExprKind::ForLoop(ref pat, ref cond, ref block, label) => {
-            ControlFlow::new_for(pat, cond, block, label, expr.span).rewrite(context, width, offset)
+            ControlFlow::new_for(pat, cond, block, label, expr.span).rewrite(context, shape)
         }
         ast::ExprKind::Loop(ref block, label) => {
-            ControlFlow::new_loop(block, label, expr.span).rewrite(context, width, offset)
+            ControlFlow::new_loop(block, label, expr.span).rewrite(context, shape)
         }
-        ast::ExprKind::Block(ref block) => block.rewrite(context, width, offset),
+        ast::ExprKind::Block(ref block) => block.rewrite(context, shape),
         ast::ExprKind::If(ref cond, ref if_block, ref else_block) => {
             ControlFlow::new_if(cond,
                                 None,
@@ -132,7 +115,7 @@ fn format_expr(expr: &ast::Expr,
                                 expr_type == ExprType::SubExpression,
                                 false,
                                 expr.span)
-                .rewrite(context, width, offset)
+                .rewrite(context, shape)
         }
         ast::ExprKind::IfLet(ref pat, ref cond, ref if_block, ref else_block) => {
             ControlFlow::new_if(cond,
@@ -142,24 +125,19 @@ fn format_expr(expr: &ast::Expr,
                                 expr_type == ExprType::SubExpression,
                                 false,
                                 expr.span)
-                .rewrite(context, width, offset)
+                .rewrite(context, shape)
         }
         ast::ExprKind::Match(ref cond, ref arms) => {
-            rewrite_match(context, cond, arms, width, offset, expr.span)
+            rewrite_match(context, cond, arms, shape, expr.span)
         }
         ast::ExprKind::Path(ref qself, ref path) => {
-            rewrite_path(context,
-                         PathContext::Expr,
-                         qself.as_ref(),
-                         path,
-                         width,
-                         offset)
+            rewrite_path(context, PathContext::Expr, qself.as_ref(), path, shape)
         }
         ast::ExprKind::Assign(ref lhs, ref rhs) => {
-            rewrite_assignment(context, lhs, rhs, None, width, offset)
+            rewrite_assignment(context, lhs, rhs, None, shape)
         }
         ast::ExprKind::AssignOp(ref op, ref lhs, ref rhs) => {
-            rewrite_assignment(context, lhs, rhs, Some(op), width, offset)
+            rewrite_assignment(context, lhs, rhs, Some(op), shape)
         }
         ast::ExprKind::Continue(ref opt_ident) => {
             let id_str = match *opt_ident {
@@ -168,8 +146,7 @@ fn format_expr(expr: &ast::Expr,
             };
             wrap_str(format!("continue{}", id_str),
                      context.config.max_width,
-                     width,
-                     offset)
+                     shape)
         }
         ast::ExprKind::Break(ref opt_ident, ref opt_expr) => {
             let id_str = match *opt_ident {
@@ -178,56 +155,40 @@ fn format_expr(expr: &ast::Expr,
             };
 
             if let Some(ref expr) = *opt_expr {
-                rewrite_unary_prefix(context,
-                                     &format!("break{} ", id_str),
-                                     &**expr,
-                                     width,
-                                     offset)
+                rewrite_unary_prefix(context, &format!("break{} ", id_str), &**expr, shape)
             } else {
-                wrap_str(format!("break{}", id_str),
-                         context.config.max_width,
-                         width,
-                         offset)
+                wrap_str(format!("break{}", id_str), context.config.max_width, shape)
             }
         }
         ast::ExprKind::Closure(capture, ref fn_decl, ref body, _) => {
-            rewrite_closure(capture, fn_decl, body, expr.span, context, width, offset)
+            rewrite_closure(capture, fn_decl, body, expr.span, context, shape)
         }
         ast::ExprKind::Try(..) |
         ast::ExprKind::Field(..) |
         ast::ExprKind::TupField(..) |
-        ast::ExprKind::MethodCall(..) => rewrite_chain(expr, context, width, offset),
+        ast::ExprKind::MethodCall(..) => rewrite_chain(expr, context, shape),
         ast::ExprKind::Mac(ref mac) => {
             // Failure to rewrite a marco should not imply failure to
             // rewrite the expression.
-            rewrite_macro(mac, None, context, width, offset, MacroPosition::Expression)
-                .or_else(|| {
-                    wrap_str(context.snippet(expr.span),
-                             context.config.max_width,
-                             width,
-                             offset)
-                })
-        }
-        ast::ExprKind::Ret(None) => {
-            wrap_str("return".to_owned(), context.config.max_width, width, offset)
+            rewrite_macro(mac, None, context, shape, MacroPosition::Expression)
+                .or_else(|| wrap_str(context.snippet(expr.span), context.config.max_width, shape))
         }
+        ast::ExprKind::Ret(None) => wrap_str("return".to_owned(), context.config.max_width, shape),
         ast::ExprKind::Ret(Some(ref expr)) => {
-            rewrite_unary_prefix(context, "return ", &**expr, width, offset)
-        }
-        ast::ExprKind::Box(ref expr) => {
-            rewrite_unary_prefix(context, "box ", &**expr, width, offset)
+            rewrite_unary_prefix(context, "return ", &**expr, shape)
         }
+        ast::ExprKind::Box(ref expr) => rewrite_unary_prefix(context, "box ", &**expr, shape),
         ast::ExprKind::AddrOf(mutability, ref expr) => {
-            rewrite_expr_addrof(context, mutability, expr, width, offset)
+            rewrite_expr_addrof(context, mutability, expr, shape)
         }
         ast::ExprKind::Cast(ref expr, ref ty) => {
-            rewrite_pair(&**expr, &**ty, "", " as ", "", context, width, offset)
+            rewrite_pair(&**expr, &**ty, "", " as ", "", context, shape)
         }
         ast::ExprKind::Type(ref expr, ref ty) => {
-            rewrite_pair(&**expr, &**ty, "", ": ", "", context, width, offset)
+            rewrite_pair(&**expr, &**ty, "", ": ", "", context, shape)
         }
         ast::ExprKind::Index(ref expr, ref index) => {
-            rewrite_index(&**expr, &**index, context, width, offset)
+            rewrite_index(&**expr, &**index, context, shape)
         }
         ast::ExprKind::Repeat(ref expr, ref repeats) => {
             let (lbr, rbr) = if context.config.spaces_within_square_brackets {
@@ -235,7 +196,7 @@ fn format_expr(expr: &ast::Expr,
             } else {
                 ("[", "]")
             };
-            rewrite_pair(&**expr, &**repeats, lbr, "; ", rbr, context, width, offset)
+            rewrite_pair(&**expr, &**repeats, lbr, "; ", rbr, context, shape)
         }
         ast::ExprKind::Range(ref lhs, ref rhs, limits) => {
             let delim = match limits {
@@ -250,7 +211,7 @@ fn format_expr(expr: &ast::Expr,
                     } else {
                         delim.into()
                     };
-                    rewrite_pair(&**lhs, &**rhs, "", &sp_delim, "", context, width, offset)
+                    rewrite_pair(&**lhs, &**rhs, "", &sp_delim, "", context, shape)
                 }
                 (None, Some(ref rhs)) => {
                     let sp_delim = if context.config.spaces_around_ranges {
@@ -258,7 +219,7 @@ fn format_expr(expr: &ast::Expr,
                     } else {
                         delim.into()
                     };
-                    rewrite_unary_prefix(context, &sp_delim, &**rhs, width, offset)
+                    rewrite_unary_prefix(context, &sp_delim, &**rhs, shape)
                 }
                 (Some(ref lhs), None) => {
                     let sp_delim = if context.config.spaces_around_ranges {
@@ -266,22 +227,19 @@ fn format_expr(expr: &ast::Expr,
                     } else {
                         delim.into()
                     };
-                    rewrite_unary_suffix(context, &sp_delim, &**lhs, width, offset)
+                    rewrite_unary_suffix(context, &sp_delim, &**lhs, shape)
                 }
-                (None, None) => wrap_str(delim.into(), context.config.max_width, width, offset),
+                (None, None) => wrap_str(delim.into(), context.config.max_width, shape),
             }
         }
         // We do not format these expressions yet, but they should still
         // satisfy our width restrictions.
         ast::ExprKind::InPlace(..) |
         ast::ExprKind::InlineAsm(..) => {
-            wrap_str(context.snippet(expr.span),
-                     context.config.max_width,
-                     width,
-                     offset)
+            wrap_str(context.snippet(expr.span), context.config.max_width, shape)
         }
     };
-    result.and_then(|res| recover_comment_removed(res, expr.span, context, width, offset))
+    result.and_then(|res| recover_comment_removed(res, expr.span, context, shape))
 }
 
 pub fn rewrite_pair<LHS, RHS>(lhs: &LHS,
@@ -290,31 +248,31 @@ pub fn rewrite_pair<LHS, RHS>(lhs: &LHS,
                               infix: &str,
                               suffix: &str,
                               context: &RewriteContext,
-                              width: usize,
-                              offset: Indent)
+                              shape: Shape)
                               -> Option<String>
     where LHS: Rewrite,
           RHS: Rewrite
 {
-    let lhs_budget = try_opt!(width.checked_sub(prefix.len() + infix.len()));
-    let rhs_budget = try_opt!(width.checked_sub(suffix.len()));
+    let lhs_budget = try_opt!(shape.width.checked_sub(prefix.len() + infix.len()));
+    let rhs_budget = try_opt!(shape.width.checked_sub(suffix.len()));
 
     // Get "full width" rhs and see if it fits on the current line. This
     // usually works fairly well since it tends to place operands of
     // operations with high precendence close together.
     // Note that this is non-conservative, but its just to see if it's even
     // worth trying to put everything on one line.
-    let rhs_result = rhs.rewrite(context, rhs_budget, offset);
+    let rhs_result = rhs.rewrite(context, Shape::legacy(rhs_budget, shape.indent));
 
     if let Some(rhs_result) = rhs_result {
         // This is needed in case of line break not caused by a
         // shortage of space, but by end-of-line comments, for example.
         if !rhs_result.contains('\n') {
-            let lhs_result = lhs.rewrite(context, lhs_budget, offset);
+            let lhs_result = lhs.rewrite(context, Shape::legacy(lhs_budget, shape.indent));
             if let Some(lhs_result) = lhs_result {
                 let mut result = format!("{}{}{}", prefix, lhs_result, infix);
 
-                let remaining_width = width.checked_sub(last_line_width(&result)).unwrap_or(0);
+                let remaining_width =
+                    shape.width.checked_sub(last_line_width(&result)).unwrap_or(0);
 
                 if rhs_result.len() <= remaining_width {
                     result.push_str(&rhs_result);
@@ -324,7 +282,9 @@ pub fn rewrite_pair<LHS, RHS>(lhs: &LHS,
 
                 // Try rewriting the rhs into the remaining space.
                 let rhs_budget = try_opt!(remaining_width.checked_sub(suffix.len()));
-                if let Some(rhs_result) = rhs.rewrite(context, rhs_budget, offset + result.len()) {
+                if let Some(rhs_result) = rhs.rewrite(context,
+                                                      Shape::legacy(rhs_budget,
+                                                                    shape.indent + result.len())) {
                     if rhs_result.len() <= remaining_width {
                         result.push_str(&rhs_result);
                         result.push_str(suffix);
@@ -339,13 +299,14 @@ pub fn rewrite_pair<LHS, RHS>(lhs: &LHS,
 
     // Re-evaluate the rhs because we have more space now:
     let infix = infix.trim_right();
-    let lhs_budget =
-        try_opt!(context.config.max_width.checked_sub(offset.width() + prefix.len() + infix.len()));
+    let lhs_budget = try_opt!(context.config
+        .max_width
+        .checked_sub(shape.indent.width() + prefix.len() + infix.len()));
     let rhs_budget = try_opt!(rhs_budget.checked_sub(prefix.len()));
-    let rhs_offset = offset + prefix.len();
+    let rhs_offset = shape.indent + prefix.len();
 
-    let rhs_result = try_opt!(rhs.rewrite(context, rhs_budget, rhs_offset));
-    let lhs_result = try_opt!(lhs.rewrite(context, lhs_budget, offset));
+    let rhs_result = try_opt!(rhs.rewrite(context, Shape::legacy(rhs_budget, rhs_offset)));
+    let lhs_result = try_opt!(lhs.rewrite(context, Shape::legacy(lhs_budget, shape.indent)));
     Some(format!("{}{}{}\n{}{}{}",
                  prefix,
                  lhs_result,
@@ -358,8 +319,7 @@ pub fn rewrite_pair<LHS, RHS>(lhs: &LHS,
 pub fn rewrite_array<'a, I>(expr_iter: I,
                             span: Span,
                             context: &RewriteContext,
-                            width: usize,
-                            offset: Indent)
+                            shape: Shape)
                             -> Option<String>
     where I: Iterator<Item = &'a ast::Expr>
 {
@@ -368,18 +328,19 @@ pub fn rewrite_array<'a, I>(expr_iter: I,
     } else {
         1 // "["
     };
-    let offset = offset + bracket_size;
+    let offset = shape.indent + bracket_size;
     let inner_context = &RewriteContext { block_indent: offset, ..*context };
-    let max_item_width = try_opt!(width.checked_sub(bracket_size * 2));
-    let items = itemize_list(context.codemap,
-                             expr_iter,
-                             "]",
-                             |item| item.span.lo,
-                             |item| item.span.hi,
-                             |item| item.rewrite(inner_context, max_item_width, offset),
-                             span.lo,
-                             span.hi)
-        .collect::<Vec<_>>();
+    let max_item_width = try_opt!(shape.width.checked_sub(bracket_size * 2));
+    let items =
+        itemize_list(context.codemap,
+                     expr_iter,
+                     "]",
+                     |item| item.span.lo,
+                     |item| item.span.hi,
+                     |item| item.rewrite(inner_context, Shape::legacy(max_item_width, offset)),
+                     span.lo,
+                     span.hi)
+            .collect::<Vec<_>>();
 
     let has_long_item = try_opt!(items.iter()
         .map(|li| li.item.as_ref().map(|s| s.len() > 10))
@@ -395,8 +356,7 @@ pub fn rewrite_array<'a, I>(expr_iter: I,
         tactic: tactic,
         separator: ",",
         trailing_separator: SeparatorTactic::Never,
-        indent: offset,
-        width: max_item_width,
+        shape: Shape::legacy(max_item_width, offset),
         ends_with_newline: false,
         config: context.config,
     };
@@ -410,7 +370,7 @@ pub fn rewrite_array<'a, I>(expr_iter: I,
 }
 
 // This functions is pretty messy because of the rules around closures and blocks:
-// TODO
+// FIXME - the below is probably no longer true in full.
 //   * if there is a return type, then there must be braces,
 //   * given a closure with braces, whether that is parsed to give an inner block
 //     or not depends on if there is a return type and if there are statements
@@ -423,34 +383,34 @@ fn rewrite_closure(capture: ast::CaptureBy,
                    body: &ast::Expr,
                    span: Span,
                    context: &RewriteContext,
-                   width: usize,
-                   offset: Indent)
+                   shape: Shape)
                    -> Option<String> {
     let mover = if capture == ast::CaptureBy::Value {
         "move "
     } else {
         ""
     };
-    let offset = offset + mover.len();
+    let offset = shape.indent + mover.len();
 
     // 4 = "|| {".len(), which is overconservative when the closure consists of
     // a single expression.
-    let budget = try_opt!(width.checked_sub(4 + mover.len()));
+    let budget = try_opt!(shape.width.checked_sub(4 + mover.len()));
     // 1 = |
     let argument_offset = offset + 1;
-    let ret_str = try_opt!(fn_decl.output.rewrite(context, budget, argument_offset));
+    let ret_str = try_opt!(fn_decl.output.rewrite(context, Shape::legacy(budget, argument_offset)));
 
     // 1 = space between arguments and return type.
     let horizontal_budget = budget.checked_sub(ret_str.len() + 1).unwrap_or(0);
 
-    let arg_items = itemize_list(context.codemap,
-                                 fn_decl.inputs.iter(),
-                                 "|",
-                                 |arg| span_lo_for_arg(arg),
-                                 |arg| span_hi_for_arg(arg),
-                                 |arg| arg.rewrite(context, budget, argument_offset),
-                                 context.codemap.span_after(span, "|"),
-                                 body.span.lo);
+    let arg_items =
+        itemize_list(context.codemap,
+                     fn_decl.inputs.iter(),
+                     "|",
+                     |arg| span_lo_for_arg(arg),
+                     |arg| span_hi_for_arg(arg),
+                     |arg| arg.rewrite(context, Shape::legacy(budget, argument_offset)),
+                     context.codemap.span_after(span, "|"),
+                     body.span.lo);
     let item_vec = arg_items.collect::<Vec<_>>();
     let tactic = definitive_tactic(&item_vec, ListTactic::HorizontalVertical, horizontal_budget);
     let budget = match tactic {
@@ -462,8 +422,7 @@ fn rewrite_closure(capture: ast::CaptureBy,
         tactic: tactic,
         separator: ",",
         trailing_separator: SeparatorTactic::Never,
-        indent: argument_offset,
-        width: budget,
+        shape: Shape::legacy(budget, argument_offset),
         ends_with_newline: false,
         config: context.config,
     };
@@ -482,7 +441,7 @@ fn rewrite_closure(capture: ast::CaptureBy,
 
     // 1 = space between `|...|` and body.
     let extra_offset = extra_offset(&prefix, offset) + 1;
-    let budget = try_opt!(width.checked_sub(extra_offset));
+    let budget = try_opt!(shape.width.checked_sub(extra_offset));
     let total_offset = offset + extra_offset;
 
     if let ast::ExprKind::Block(ref block) = body.node {
@@ -502,8 +461,7 @@ fn rewrite_closure(capture: ast::CaptureBy,
                 if let Some(rw) = rewrite_closure_expr(expr,
                                                        &prefix,
                                                        context,
-                                                       budget,
-                                                       total_offset) {
+                                                       Shape::legacy(budget, total_offset)) {
                     return Some(rw);
                 }
             }
@@ -513,7 +471,9 @@ fn rewrite_closure(capture: ast::CaptureBy,
             // We need braces, but we might still prefer a one-liner.
             let stmt = &block.stmts[0];
             // 4 = braces and spaces.
-            let mut rewrite = stmt.rewrite(context, try_opt!(budget.checked_sub(4)), total_offset);
+            let mut rewrite = stmt.rewrite(context,
+                                           Shape::legacy(try_opt!(budget.checked_sub(4)),
+                                                         total_offset));
 
             // Checks if rewrite succeeded and fits on a single line.
             rewrite = and_one_line(rewrite);
@@ -527,7 +487,10 @@ fn rewrite_closure(capture: ast::CaptureBy,
         return rewrite_closure_block(&block, prefix, context, budget);
     }
 
-    if let Some(rw) = rewrite_closure_expr(body, &prefix, context, budget, total_offset) {
+    if let Some(rw) = rewrite_closure_expr(body,
+                                           &prefix,
+                                           context,
+                                           Shape::legacy(budget, total_offset)) {
         return Some(rw);
     }
 
@@ -548,10 +511,9 @@ fn rewrite_closure(capture: ast::CaptureBy,
     fn rewrite_closure_expr(expr: &ast::Expr,
                             prefix: &str,
                             context: &RewriteContext,
-                            budget: usize,
-                            offset: Indent)
+                            shape: Shape)
                             -> Option<String> {
-        let mut rewrite = expr.rewrite(context, budget, offset);
+        let mut rewrite = expr.rewrite(context, shape);
         if classify::expr_requires_semi_to_be_stmt(left_most_sub_expr(expr)) {
             rewrite = and_one_line(rewrite);
         }
@@ -565,7 +527,7 @@ fn rewrite_closure(capture: ast::CaptureBy,
                              -> Option<String> {
         // Start with visual indent, then fall back to block indent if the
         // closure is large.
-        let rewrite = try_opt!(block.rewrite(&context, budget, Indent::empty()));
+        let rewrite = try_opt!(block.rewrite(&context, Shape::legacy(budget, Indent::empty())));
 
         let block_threshold = context.config.closure_block_indent_threshold;
         if block_threshold < 0 || rewrite.matches('\n').count() <= block_threshold as usize {
@@ -576,7 +538,7 @@ fn rewrite_closure(capture: ast::CaptureBy,
         // means we must re-format.
         let mut context = context.clone();
         context.block_indent.alignment = 0;
-        let rewrite = try_opt!(block.rewrite(&context, budget, Indent::empty()));
+        let rewrite = try_opt!(block.rewrite(&context, Shape::legacy(budget, Indent::empty())));
         Some(format!("{} {}", prefix, rewrite))
     }
 }
@@ -597,11 +559,12 @@ fn nop_block_collapse(block_str: Option<String>, budget: usize) -> Option<String
 }
 
 impl Rewrite for ast::Block {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
-        // width is used only for the single line case: either the empty block `{}`,
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
+        // shape.width is used only for the single line case: either the empty block `{}`,
         // or an unsafe expression `unsafe { e }`.
 
-        if self.stmts.is_empty() && !block_contains_comment(self, context.codemap) && width >= 2 {
+        if self.stmts.is_empty() && !block_contains_comment(self, context.codemap) &&
+           shape.width >= 2 {
             return Some("{}".to_owned());
         }
 
@@ -611,7 +574,8 @@ impl Rewrite for ast::Block {
         if user_str.starts_with('{') && user_str.ends_with('}') {
             let comment_str = user_str[1..user_str.len() - 1].trim();
             if self.stmts.is_empty() && !comment_str.contains('\n') &&
-               !comment_str.starts_with("//") && comment_str.len() + 4 <= width {
+               !comment_str.starts_with("//") &&
+               comment_str.len() + 4 <= shape.width {
                 return Some(format!("{{ {} }}", comment_str));
             }
         }
@@ -630,22 +594,23 @@ impl Rewrite for ast::Block {
 
                 let prefix = if !trimmed.is_empty() {
                     // 9 = "unsafe  {".len(), 7 = "unsafe ".len()
-                    let budget = try_opt!(width.checked_sub(9));
+                    let budget = try_opt!(shape.width.checked_sub(9));
                     format!("unsafe {} ",
                             try_opt!(rewrite_comment(trimmed,
                                                      true,
-                                                     budget,
-                                                     offset + 7,
+                                                     Shape::legacy(budget, shape.indent + 7),
                                                      context.config)))
                 } else {
                     "unsafe ".to_owned()
                 };
 
-                if is_simple_block(self, context.codemap) && prefix.len() < width {
-                    let expr_str = self.stmts[0].rewrite(context, width - prefix.len(), offset);
+                if is_simple_block(self, context.codemap) && prefix.len() < shape.width {
+                    let expr_str = self.stmts[0].rewrite(context,
+                                                         Shape::legacy(shape.width - prefix.len(),
+                                                                       shape.indent));
                     let expr_str = try_opt!(expr_str);
                     let result = format!("{}{{ {} }}", prefix, expr_str);
-                    if result.len() <= width && !result.contains('\n') {
+                    if result.len() <= shape.width && !result.contains('\n') {
                         return Some(result);
                     }
                 }
@@ -666,10 +631,11 @@ impl Rewrite for ast::Block {
 }
 
 impl Rewrite for ast::Stmt {
-    fn rewrite(&self, context: &RewriteContext, _width: usize, offset: Indent) -> Option<String> {
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
         let result = match self.node {
             ast::StmtKind::Local(ref local) => {
-                local.rewrite(context, context.config.max_width, offset)
+                local.rewrite(context,
+                              Shape::legacy(context.config.max_width, shape.indent))
             }
             ast::StmtKind::Expr(ref ex) |
             ast::StmtKind::Semi(ref ex) => {
@@ -682,14 +648,15 @@ impl Rewrite for ast::Stmt {
                                 _ => unreachable!(),
                             },
                             context,
-                            context.config.max_width - offset.width() - suffix.len(),
-                            offset)
+                            Shape::legacy(context.config.max_width - shape.indent.width() -
+                                          suffix.len(),
+                                          shape.indent))
                     .map(|s| s + suffix)
             }
             ast::StmtKind::Mac(..) |
             ast::StmtKind::Item(..) => None,
         };
-        result.and_then(|res| recover_comment_removed(res, self.span, context, _width, offset))
+        result.and_then(|res| recover_comment_removed(res, self.span, context, shape))
     }
 }
 
@@ -819,11 +786,12 @@ impl<'a> ControlFlow<'a> {
 
             let new_width = try_opt!(width.checked_sub(pat_expr_str.len() + fixed_cost));
             let expr = &self.block.stmts[0];
-            let if_str = try_opt!(expr.rewrite(context, new_width, Indent::empty()));
+            let if_str = try_opt!(expr.rewrite(context, Shape::legacy(new_width, Indent::empty())));
 
             let new_width = try_opt!(new_width.checked_sub(if_str.len()));
             let else_expr = &else_node.stmts[0];
-            let else_str = try_opt!(else_expr.rewrite(context, new_width, Indent::empty()));
+            let else_str =
+                try_opt!(else_expr.rewrite(context, Shape::legacy(new_width, Indent::empty())));
 
             if if_str.contains('\n') || else_str.contains('\n') {
                 return None;
@@ -845,14 +813,14 @@ impl<'a> ControlFlow<'a> {
 }
 
 impl<'a> Rewrite for ControlFlow<'a> {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
-        debug!("ControlFlow::rewrite {:?} {} {:?}", self, width, offset);
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
+        debug!("ControlFlow::rewrite {:?} {:?}", self, shape);
         let (budget, indent) = if self.nested_if {
             // We are part of an if-elseif-else chain. Our constraints are tightened.
             // 7 = "} else " .len()
-            (try_opt!(width.checked_sub(7)), offset + 7)
+            (try_opt!(shape.width.checked_sub(7)), shape.indent + 7)
         } else {
-            (width, offset)
+            (shape.width, shape.indent)
         };
 
         let label_string = rewrite_label(self.label);
@@ -872,15 +840,14 @@ impl<'a> Rewrite for ControlFlow<'a> {
                                           cond,
                                           self.matcher,
                                           self.connector,
-                                          inner_width,
-                                          inner_offset))
+                                          Shape::legacy(inner_width, inner_offset)))
             }
             None => String::new(),
         };
 
         // Try to format if-else on single line.
         if self.allow_single_line && context.config.single_line_if_else_max_width > 0 {
-            let trial = self.rewrite_single_line(&pat_expr_string, context, width);
+            let trial = self.rewrite_single_line(&pat_expr_string, context, shape.width);
 
             if trial.is_some() &&
                trial.as_ref().unwrap().len() <= context.config.single_line_if_else_max_width {
@@ -891,16 +858,18 @@ impl<'a> Rewrite for ControlFlow<'a> {
         // This is used only for the empty block case: `{}`. So, we use 1 if we know
         // we should avoid the single line case.
         // 2 = spaces after keyword and condition.
-        let block_width = try_opt!(width.checked_sub(label_string.len() + self.keyword.len() +
-                                                     extra_offset(&pat_expr_string, inner_offset) +
-                                                     2));
+        let block_width = try_opt!(shape.width
+            .checked_sub(label_string.len() + self.keyword.len() +
+                         extra_offset(&pat_expr_string, inner_offset) +
+                         2));
         let block_width = if self.else_block.is_some() || self.nested_if {
             min(1, block_width)
         } else {
             block_width
         };
 
-        let block_str = try_opt!(self.block.rewrite(context, block_width, offset));
+        let block_str = try_opt!(self.block
+            .rewrite(context, Shape::legacy(block_width, shape.indent)));
 
         let cond_span = if let Some(cond) = self.cond {
             cond.span
@@ -917,12 +886,10 @@ impl<'a> Rewrite for ControlFlow<'a> {
                       context.codemap.span_before(self.span, self.matcher.trim())
                   }));
 
-        let between_kwd_cond_comment = extract_comment(between_kwd_cond, context, offset, width);
+        let between_kwd_cond_comment = extract_comment(between_kwd_cond, context, shape);
 
-        let after_cond_comment = extract_comment(mk_sp(cond_span.hi, self.block.span.lo),
-                                                 context,
-                                                 offset,
-                                                 width);
+        let after_cond_comment =
+            extract_comment(mk_sp(cond_span.hi, self.block.span.lo), context, shape);
 
         let alt_block_sep = String::from("\n") + &context.block_indent.to_string(context.config);
         let block_sep = if self.cond.is_none() && between_kwd_cond_comment.is_some() {
@@ -949,7 +916,7 @@ impl<'a> Rewrite for ControlFlow<'a> {
             let rewrite = match else_block.node {
                 // If the else expression is another if-else expression, prevent it
                 // from being formatted on a single line.
-                // Note how we're passing the original width and offset, as the
+                // Note how we're passing the original shape, as the
                 // cost of "else" should not cascade.
                 ast::ExprKind::IfLet(ref pat, ref cond, ref if_block, ref next_else_block) => {
                     ControlFlow::new_if(cond,
@@ -959,7 +926,7 @@ impl<'a> Rewrite for ControlFlow<'a> {
                                         false,
                                         true,
                                         mk_sp(else_block.span.lo, self.span.hi))
-                        .rewrite(context, width, offset)
+                        .rewrite(context, shape)
                 }
                 ast::ExprKind::If(ref cond, ref if_block, ref next_else_block) => {
                     ControlFlow::new_if(cond,
@@ -969,13 +936,13 @@ impl<'a> Rewrite for ControlFlow<'a> {
                                         false,
                                         true,
                                         mk_sp(else_block.span.lo, self.span.hi))
-                        .rewrite(context, width, offset)
+                        .rewrite(context, shape)
                 }
                 _ => {
                     last_in_chain = true;
                     // When rewriting a block, the width is only used for single line
                     // blocks, passing 1 lets us avoid that.
-                    else_block.rewrite(context, min(1, width), offset)
+                    else_block.rewrite(context, Shape::legacy(min(1, shape.width), shape.indent))
                 }
             };
 
@@ -984,13 +951,13 @@ impl<'a> Rewrite for ControlFlow<'a> {
                       context.codemap
                           .span_before(mk_sp(self.block.span.hi, else_block.span.lo), "else"));
             let between_kwd_else_block_comment =
-                extract_comment(between_kwd_else_block, context, offset, width);
+                extract_comment(between_kwd_else_block, context, shape);
 
             let after_else = mk_sp(context.codemap
                                        .span_after(mk_sp(self.block.span.hi, else_block.span.lo),
                                                    "else"),
                                    else_block.span.lo);
-            let after_else_comment = extract_comment(after_else, context, offset, width);
+            let after_else_comment = extract_comment(after_else, context, shape);
 
             let between_sep = match context.config.control_brace_style {
                 ControlBraceStyle::AlwaysNextLine |
@@ -1021,18 +988,13 @@ fn rewrite_label(label: Option<ast::SpannedIdent>) -> String {
     }
 }
 
-fn extract_comment(span: Span,
-                   context: &RewriteContext,
-                   offset: Indent,
-                   width: usize)
-                   -> Option<String> {
+fn extract_comment(span: Span, context: &RewriteContext, shape: Shape) -> Option<String> {
     let comment_str = context.snippet(span);
     if contains_comment(&comment_str) {
-        let comment =
-            try_opt!(rewrite_comment(comment_str.trim(), false, width, offset, context.config));
+        let comment = try_opt!(rewrite_comment(comment_str.trim(), false, shape, context.config));
         Some(format!("\n{indent}{}\n{indent}",
                      comment,
-                     indent = offset.to_string(context.config)))
+                     indent = shape.indent.to_string(context.config)))
     } else {
         None
     }
@@ -1081,8 +1043,7 @@ fn is_unsafe_block(block: &ast::Block) -> bool {
 //    are about the second arm
 fn rewrite_match_arm_comment(context: &RewriteContext,
                              missed_str: &str,
-                             width: usize,
-                             arm_indent: Indent,
+                             shape: Shape,
                              arm_indent_str: &str)
                              -> Option<String> {
     // The leading "," is not part of the arm-comment
@@ -1105,8 +1066,7 @@ fn rewrite_match_arm_comment(context: &RewriteContext,
     }
     let missed_str = missed_str[first..].trim();
     if !missed_str.is_empty() {
-        let comment =
-            try_opt!(rewrite_comment(&missed_str, false, width, arm_indent, context.config));
+        let comment = try_opt!(rewrite_comment(&missed_str, false, shape, context.config));
         result.push('\n');
         result.push_str(arm_indent_str);
         result.push_str(&comment);
@@ -1118,8 +1078,7 @@ fn rewrite_match_arm_comment(context: &RewriteContext,
 fn rewrite_match(context: &RewriteContext,
                  cond: &ast::Expr,
                  arms: &[ast::Arm],
-                 width: usize,
-                 offset: Indent,
+                 shape: Shape,
                  span: Span)
                  -> Option<String> {
     if arms.is_empty() {
@@ -1127,8 +1086,8 @@ fn rewrite_match(context: &RewriteContext,
     }
 
     // `match `cond` {`
-    let cond_budget = try_opt!(width.checked_sub(8));
-    let cond_str = try_opt!(cond.rewrite(context, cond_budget, offset + 6));
+    let cond_budget = try_opt!(shape.width.checked_sub(8));
+    let cond_str = try_opt!(cond.rewrite(context, Shape::legacy(cond_budget, shape.indent + 6)));
     let alt_block_sep = String::from("\n") + &context.block_indent.to_string(context.config);
     let block_sep = match context.config.control_brace_style {
         ControlBraceStyle::AlwaysSameLine => " ",
@@ -1152,16 +1111,15 @@ fn rewrite_match(context: &RewriteContext,
         };
         let comment = try_opt!(rewrite_match_arm_comment(context,
                                                          &missed_str,
-                                                         width,
-                                                         arm_indent,
+                                                         Shape::legacy(shape.width, arm_indent),
                                                          &arm_indent_str));
         result.push_str(&comment);
         result.push('\n');
         result.push_str(&arm_indent_str);
 
         let arm_str = arm.rewrite(&nested_context,
-                                  context.config.max_width - arm_indent.width(),
-                                  arm_indent);
+                                  Shape::legacy(context.config.max_width - arm_indent.width(),
+                                                arm_indent));
         if let Some(ref arm_str) = arm_str {
             result.push_str(arm_str);
         } else {
@@ -1176,8 +1134,7 @@ fn rewrite_match(context: &RewriteContext,
     let last_comment = context.snippet(last_span);
     let comment = try_opt!(rewrite_match_arm_comment(context,
                                                      &last_comment,
-                                                     width,
-                                                     arm_indent,
+                                                     Shape::legacy(shape.width, arm_indent),
                                                      &arm_indent_str));
     result.push_str(&comment);
     result.push('\n');
@@ -1221,8 +1178,8 @@ fn arm_comma(config: &Config, arm: &ast::Arm, body: &ast::Expr) -> &'static str
 
 // Match arms.
 impl Rewrite for ast::Arm {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
-        debug!("Arm::rewrite {:?} {} {:?}", self, width, offset);
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
+        debug!("Arm::rewrite {:?} {:?}", self, shape);
         let &ast::Arm { ref attrs, ref pats, ref guard, ref body } = self;
 
         // FIXME this is all a bit grotty, would be nice to abstract out the
@@ -1245,9 +1202,9 @@ impl Rewrite for ast::Arm {
 
         // Patterns
         // 5 = ` => {`
-        let pat_budget = try_opt!(width.checked_sub(5));
+        let pat_budget = try_opt!(shape.width.checked_sub(5));
         let pat_strs = try_opt!(pats.iter()
-            .map(|p| p.rewrite(context, pat_budget, offset))
+            .map(|p| p.rewrite(context, Shape::legacy(pat_budget, shape.indent)))
             .collect::<Option<Vec<_>>>());
 
         let all_simple = pat_strs.iter().all(|p| pat_is_simple(p));
@@ -1260,30 +1217,28 @@ impl Rewrite for ast::Arm {
             },
             separator: " |",
             trailing_separator: SeparatorTactic::Never,
-            indent: offset,
-            width: pat_budget,
+            shape: Shape::legacy(pat_budget, shape.indent),
             ends_with_newline: false,
             config: context.config,
         };
         let pats_str = try_opt!(write_list(items, &fmt));
 
         let budget = if pats_str.contains('\n') {
-            context.config.max_width - offset.width()
+            context.config.max_width - shape.indent.width()
         } else {
-            width
+            shape.width
         };
 
         let guard_str = try_opt!(rewrite_guard(context,
                                                guard,
-                                               budget,
-                                               offset,
+                                               Shape::legacy(budget, shape.indent),
                                                trimmed_last_line_width(&pats_str)));
 
         let pats_str = format!("{}{}", pats_str, guard_str);
         // Where the next text can start.
         let mut line_start = last_line_width(&pats_str);
         if !pats_str.contains('\n') {
-            line_start += offset.width();
+            line_start += shape.indent.width();
         }
 
         let body = match body.node {
@@ -1306,8 +1261,10 @@ impl Rewrite for ast::Arm {
         // 4 = ` => `.len()
         if context.config.max_width > line_start + comma.len() + 4 {
             let budget = context.config.max_width - line_start - comma.len() - 4;
-            let offset = Indent::new(offset.block_indent, line_start + 4 - offset.block_indent);
-            let rewrite = nop_block_collapse(body.rewrite(context, budget, offset), budget);
+            let offset = Indent::new(shape.indent.block_indent,
+                                     line_start + 4 - shape.indent.block_indent);
+            let rewrite = nop_block_collapse(body.rewrite(context, Shape::legacy(budget, offset)),
+                                             budget);
             let is_block = if let ast::ExprKind::Block(..) = body.node {
                 true
             } else {
@@ -1335,13 +1292,14 @@ impl Rewrite for ast::Arm {
 
         // FIXME: we're doing a second rewrite of the expr; This may not be
         // necessary.
-        let body_budget = try_opt!(width.checked_sub(context.config.tab_spaces));
+        let body_budget = try_opt!(shape.width.checked_sub(context.config.tab_spaces));
         let indent = context.block_indent.block_indent(context.config);
         let inner_context = &RewriteContext { block_indent: indent, ..*context };
-        let next_line_body =
-            try_opt!(nop_block_collapse(body.rewrite(inner_context, body_budget, indent),
-                                        body_budget));
-        let indent_str = offset.block_indent(context.config).to_string(context.config);
+        let next_line_body = try_opt!(nop_block_collapse(body.rewrite(inner_context,
+                                                                      Shape::legacy(body_budget,
+                                                                                    indent)),
+                                                         body_budget));
+        let indent_str = shape.indent.block_indent(context.config).to_string(context.config);
         let (body_prefix, body_suffix) = if context.config.wrap_match_arms {
             if context.config.match_block_trailing_comma {
                 ("{", "},")
@@ -1362,7 +1320,7 @@ impl Rewrite for ast::Arm {
                      block_sep,
                      indent_str,
                      next_line_body,
-                     offset.to_string(context.config),
+                     shape.indent.to_string(context.config),
                      body_suffix))
     }
 }
@@ -1377,8 +1335,7 @@ fn pat_is_simple(pat_str: &str) -> bool {
 // The `if ...` guard on a match arm.
 fn rewrite_guard(context: &RewriteContext,
                  guard: &Option<ptr::P<ast::Expr>>,
-                 width: usize,
-                 offset: Indent,
+                 shape: Shape,
                  // The amount of space used up on this line for the pattern in
                  // the arm (excludes offset).
                  pattern_width: usize)
@@ -1387,23 +1344,28 @@ fn rewrite_guard(context: &RewriteContext,
         // First try to fit the guard string on the same line as the pattern.
         // 4 = ` if `, 5 = ` => {`
         let overhead = pattern_width + 4 + 5;
-        if overhead < width {
-            let cond_str = guard.rewrite(context, width - overhead, offset + pattern_width + 4);
+        if overhead < shape.width {
+            let cond_str = guard.rewrite(context,
+                                         Shape::legacy(shape.width - overhead,
+                                                       shape.indent + pattern_width + 4));
             if let Some(cond_str) = cond_str {
                 return Some(format!(" if {}", cond_str));
             }
         }
 
         // Not enough space to put the guard after the pattern, try a newline.
-        let overhead = offset.block_indent(context.config).width() + 4 + 5;
-        if overhead < width {
+        let overhead = shape.indent.block_indent(context.config).width() + 4 + 5;
+        if overhead < shape.width {
             let cond_str = guard.rewrite(context,
-                                         width - overhead,
-                                         // 3 == `if `
-                                         offset.block_indent(context.config) + 3);
+                                         Shape::legacy(shape.width - overhead,
+                                                       // 3 == `if `
+                                                       shape.indent.block_indent(context.config) +
+                                                       3));
             if let Some(cond_str) = cond_str {
                 return Some(format!("\n{}if {}",
-                                    offset.block_indent(context.config).to_string(context.config),
+                                    shape.indent
+                                        .block_indent(context.config)
+                                        .to_string(context.config),
                                     cond_str));
             }
         }
@@ -1421,8 +1383,7 @@ fn rewrite_pat_expr(context: &RewriteContext,
                     // Connecting piece between pattern and expression,
                     // *without* trailing space.
                     connector: &str,
-                    width: usize,
-                    offset: Indent)
+                    shape: Shape)
                     -> Option<String> {
     let mut result = match pat {
         Some(pat) => {
@@ -1431,29 +1392,33 @@ fn rewrite_pat_expr(context: &RewriteContext,
             } else {
                 format!("{} ", matcher)
             };
-            let pat_budget = try_opt!(width.checked_sub(connector.len() + matcher.len()));
-            let pat_offset = offset + matcher.len();
-            let pat_string = try_opt!(pat.rewrite(context, pat_budget, pat_offset));
+            let pat_budget = try_opt!(shape.width.checked_sub(connector.len() + matcher.len()));
+            let pat_offset = shape.indent + matcher.len();
+            let pat_string = try_opt!(pat.rewrite(context, Shape::legacy(pat_budget, pat_offset)));
             format!("{}{}{}", matcher, pat_string, connector)
         }
         None => String::new(),
     };
 
     // Consider only the last line of the pat string.
-    let extra_offset = extra_offset(&result, offset);
+    let extra_offset = extra_offset(&result, shape.indent);
 
     // The expression may (partionally) fit on the current line.
-    if width > extra_offset + 1 {
+    if shape.width > extra_offset + 1 {
         let spacer = if pat.is_some() { " " } else { "" };
 
         let expr_rewrite = expr.rewrite(context,
-                                        try_opt!(width.checked_sub(extra_offset + spacer.len())),
-                                        offset + extra_offset + spacer.len());
+                                        Shape::legacy(try_opt!(shape.width
+                                                          .checked_sub(extra_offset +
+                                                                       spacer.len())),
+                                                      shape.indent + extra_offset + spacer.len()));
 
         if let Some(expr_string) = expr_rewrite {
-            let pat_simple =
-                pat.and_then(|p| p.rewrite(context, context.config.max_width, Indent::empty()))
-                    .map(|s| pat_is_simple(&s));
+            let pat_simple = pat.and_then(|p| {
+                    p.rewrite(context,
+                              Shape::legacy(context.config.max_width, Indent::empty()))
+                })
+                .map(|s| pat_is_simple(&s));
 
             if pat.is_none() || pat_simple.unwrap_or(false) || !expr_string.contains('\n') {
                 result.push_str(spacer);
@@ -1469,20 +1434,17 @@ fn rewrite_pat_expr(context: &RewriteContext,
     result.push('\n');
     result.push_str(&nested.block_indent.to_string(context.config));
 
-    let expr_rewrite =
-        expr.rewrite(&nested,
-                     try_opt!(context.config.max_width.checked_sub(nested.block_indent.width())),
-                     nested.block_indent);
+    let expr_rewrite = expr.rewrite(&nested,
+                                    Shape::legacy(try_opt!(context.config
+                                                      .max_width
+                                                      .checked_sub(nested.block_indent.width())),
+                                                  nested.block_indent));
     result.push_str(&try_opt!(expr_rewrite));
 
     Some(result)
 }
 
-fn rewrite_string_lit(context: &RewriteContext,
-                      span: Span,
-                      width: usize,
-                      offset: Indent)
-                      -> Option<String> {
+fn rewrite_string_lit(context: &RewriteContext, span: Span, shape: Shape) -> Option<String> {
     let string_lit = context.snippet(span);
 
     if !context.config.format_strings && !context.config.force_format_strings {
@@ -1490,7 +1452,7 @@ fn rewrite_string_lit(context: &RewriteContext,
     }
 
     if !context.config.force_format_strings &&
-       !string_requires_rewrite(context, span, &string_lit, width, offset) {
+       !string_requires_rewrite(context, span, &string_lit, shape) {
         return Some(string_lit);
     }
 
@@ -1499,8 +1461,7 @@ fn rewrite_string_lit(context: &RewriteContext,
         closer: "\"",
         line_start: " ",
         line_end: "\\",
-        width: width,
-        offset: offset,
+        shape: shape,
         trim_end: false,
         config: context.config,
     };
@@ -1514,20 +1475,19 @@ fn rewrite_string_lit(context: &RewriteContext,
 fn string_requires_rewrite(context: &RewriteContext,
                            span: Span,
                            string: &str,
-                           width: usize,
-                           offset: Indent)
+                           shape: Shape)
                            -> bool {
-    if context.codemap.lookup_char_pos(span.lo).col.0 != offset.width() {
+    if context.codemap.lookup_char_pos(span.lo).col.0 != shape.indent.width() {
         return true;
     }
 
     for (i, line) in string.lines().enumerate() {
         if i == 0 {
-            if line.len() > width {
+            if line.len() > shape.width {
                 return true;
             }
         } else {
-            if line.len() > width + offset.width() {
+            if line.len() > shape.width + shape.indent.width() {
                 return true;
             }
         }
@@ -1540,17 +1500,15 @@ pub fn rewrite_call<R>(context: &RewriteContext,
                        callee: &R,
                        args: &[ptr::P<ast::Expr>],
                        span: Span,
-                       width: usize,
-                       offset: Indent)
+                       shape: Shape)
                        -> Option<String>
     where R: Rewrite
 {
-    let closure = |callee_max_width| {
-        rewrite_call_inner(context, callee, callee_max_width, args, span, width, offset)
-    };
+    let closure =
+        |callee_max_width| rewrite_call_inner(context, callee, callee_max_width, args, span, shape);
 
     // 2 is for parens
-    let max_width = try_opt!(width.checked_sub(2));
+    let max_width = try_opt!(shape.width.checked_sub(2));
     binary_search(1, max_width, closure)
 }
 
@@ -1559,15 +1517,14 @@ fn rewrite_call_inner<R>(context: &RewriteContext,
                          max_callee_width: usize,
                          args: &[ptr::P<ast::Expr>],
                          span: Span,
-                         width: usize,
-                         offset: Indent)
+                         shape: Shape)
                          -> Result<String, Ordering>
     where R: Rewrite
 {
     let callee = callee.borrow();
     // FIXME using byte lens instead of char lens (and probably all over the
     // place too)
-    let callee_str = match callee.rewrite(context, max_callee_width, offset) {
+    let callee_str = match callee.rewrite(context, Shape::legacy(max_callee_width, shape.indent)) {
         Some(string) => {
             if !string.contains('\n') && string.len() > max_callee_width {
                 panic!("{:?} {}", string, max_callee_width);
@@ -1581,13 +1538,13 @@ fn rewrite_call_inner<R>(context: &RewriteContext,
     let span_lo = context.codemap.span_after(span, "(");
     let span = mk_sp(span_lo, span.hi);
 
-    let extra_offset = extra_offset(&callee_str, offset);
+    let extra_offset = extra_offset(&callee_str, shape.indent);
     // 2 is for parens.
-    let remaining_width = match width.checked_sub(extra_offset + 2) {
+    let remaining_width = match shape.width.checked_sub(extra_offset + 2) {
         Some(str) => str,
         None => return Err(Ordering::Greater),
     };
-    let offset = offset + extra_offset + 1;
+    let offset = shape.indent + extra_offset + 1;
     let arg_count = args.len();
     let block_indent = if arg_count == 1 {
         context.block_indent
@@ -1596,14 +1553,15 @@ fn rewrite_call_inner<R>(context: &RewriteContext,
     };
     let inner_context = &RewriteContext { block_indent: block_indent, ..*context };
 
-    let items = itemize_list(context.codemap,
-                             args.iter(),
-                             ")",
-                             |item| item.span.lo,
-                             |item| item.span.hi,
-                             |item| item.rewrite(inner_context, remaining_width, offset),
-                             span.lo,
-                             span.hi);
+    let items =
+        itemize_list(context.codemap,
+                     args.iter(),
+                     ")",
+                     |item| item.span.lo,
+                     |item| item.span.hi,
+                     |item| item.rewrite(inner_context, Shape::legacy(remaining_width, offset)),
+                     span.lo,
+                     span.hi);
     let mut item_vec: Vec<_> = items.collect();
 
     // Try letting the last argument overflow to the next line with block
@@ -1622,7 +1580,8 @@ fn rewrite_call_inner<R>(context: &RewriteContext,
     // first arguments.
     if overflow_last {
         let inner_context = &RewriteContext { block_indent: context.block_indent, ..*context };
-        let rewrite = args.last().unwrap().rewrite(inner_context, remaining_width, offset);
+        let rewrite =
+            args.last().unwrap().rewrite(inner_context, Shape::legacy(remaining_width, offset));
 
         if let Some(rewrite) = rewrite {
             let rewrite_first_line = Some(rewrite[..first_line_width(&rewrite)].to_owned());
@@ -1654,8 +1613,7 @@ fn rewrite_call_inner<R>(context: &RewriteContext,
         tactic: tactic,
         separator: ",",
         trailing_separator: SeparatorTactic::Never,
-        indent: offset,
-        width: width,
+        shape: Shape::legacy(shape.width, offset),
         ends_with_newline: false,
         config: context.config,
     };
@@ -1672,15 +1630,13 @@ fn rewrite_call_inner<R>(context: &RewriteContext,
     })
 }
 
-fn rewrite_paren(context: &RewriteContext,
-                 subexpr: &ast::Expr,
-                 width: usize,
-                 offset: Indent)
-                 -> Option<String> {
-    debug!("rewrite_paren, width: {}, offset: {:?}", width, offset);
+fn rewrite_paren(context: &RewriteContext, subexpr: &ast::Expr, shape: Shape) -> Option<String> {
+    debug!("rewrite_paren, shape: {:?}", shape);
     // 1 is for opening paren, 2 is for opening+closing, we want to keep the closing
     // paren on the same line as the subexpr.
-    let subexpr_str = subexpr.rewrite(context, try_opt!(width.checked_sub(2)), offset + 1);
+    let subexpr_str = subexpr.rewrite(context,
+                                      Shape::legacy(try_opt!(shape.width.checked_sub(2)),
+                                                    shape.indent + 1));
     debug!("rewrite_paren, subexpr_str: `{:?}`", subexpr_str);
 
     subexpr_str.map(|s| if context.config.spaces_within_parens && s.len() > 0 {
@@ -1693,10 +1649,9 @@ fn rewrite_paren(context: &RewriteContext,
 fn rewrite_index(expr: &ast::Expr,
                  index: &ast::Expr,
                  context: &RewriteContext,
-                 width: usize,
-                 offset: Indent)
+                 shape: Shape)
                  -> Option<String> {
-    let expr_str = try_opt!(expr.rewrite(context, width, offset));
+    let expr_str = try_opt!(expr.rewrite(context, shape));
 
     let (lbr, rbr) = if context.config.spaces_within_square_brackets {
         ("[ ", " ]")
@@ -1704,19 +1659,19 @@ fn rewrite_index(expr: &ast::Expr,
         ("[", "]")
     };
 
-    let budget = width.checked_sub(expr_str.len() + lbr.len() + rbr.len()).unwrap_or(0);
-    let index_str = index.rewrite(context, budget, offset);
+    let budget = shape.width.checked_sub(expr_str.len() + lbr.len() + rbr.len()).unwrap_or(0);
+    let index_str = index.rewrite(context, Shape::legacy(budget, shape.indent));
     if let Some(index_str) = index_str {
         return Some(format!("{}{}{}{}", expr_str, lbr, index_str, rbr));
     }
 
-    let indent = offset.block_indent(&context.config);
+    let indent = shape.indent.block_indent(&context.config);
     let indent = indent.to_string(&context.config);
-    // FIXME this is not right, since we don't take into account that width
+    // FIXME this is not right, since we don't take into account that shape.width
     // might be reduced from max_width by something on the right.
     let budget =
         try_opt!(context.config.max_width.checked_sub(indent.len() + lbr.len() + rbr.len()));
-    let index_str = try_opt!(index.rewrite(context, budget, offset));
+    let index_str = try_opt!(index.rewrite(context, Shape::legacy(budget, shape.indent)));
     Some(format!("{}\n{}{}{}{}", expr_str, indent, lbr, index_str, rbr))
 }
 
@@ -1725,10 +1680,9 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
                           fields: &'a [ast::Field],
                           base: Option<&'a ast::Expr>,
                           span: Span,
-                          width: usize,
-                          offset: Indent)
+                          shape: Shape)
                           -> Option<String> {
-    debug!("rewrite_struct_lit: width {}, offset {:?}", width, offset);
+    debug!("rewrite_struct_lit: shape {:?}", shape);
 
     enum StructLitField<'a> {
         Regular(&'a ast::Field),
@@ -1736,15 +1690,18 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
     }
 
     // 2 = " {".len()
-    let path_budget = try_opt!(width.checked_sub(2));
-    let path_str =
-        try_opt!(rewrite_path(context, PathContext::Expr, None, path, path_budget, offset));
+    let path_budget = try_opt!(shape.width.checked_sub(2));
+    let path_str = try_opt!(rewrite_path(context,
+                                         PathContext::Expr,
+                                         None,
+                                         path,
+                                         Shape::legacy(path_budget, shape.indent)));
 
     // Foo { a: Foo } - indent is +3, width is -5.
-    let h_budget = width.checked_sub(path_str.len() + 5).unwrap_or(0);
+    let h_budget = shape.width.checked_sub(path_str.len() + 5).unwrap_or(0);
     // The 1 taken from the v_budget is for the comma.
     let (indent, v_budget) = match context.config.struct_lit_style {
-        StructLitStyle::Visual => (offset + path_str.len() + 3, h_budget),
+        StructLitStyle::Visual => (shape.indent + path_str.len() + 3, h_budget),
         StructLitStyle::Block => {
             // If we are all on one line, then we'll ignore the indent, and we
             // have a smaller budget.
@@ -1783,12 +1740,12 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
             StructLitField::Regular(field) => {
                 rewrite_field(inner_context,
                               field,
-                              v_budget.checked_sub(1).unwrap_or(0),
-                              indent)
+                              Shape::legacy(v_budget.checked_sub(1).unwrap_or(0), indent))
             }
             StructLitField::Base(expr) => {
                 // 2 = ..
-                expr.rewrite(inner_context, try_opt!(v_budget.checked_sub(2)), indent + 2)
+                expr.rewrite(inner_context,
+                             Shape::legacy(try_opt!(v_budget.checked_sub(2)), indent + 2))
                     .map(|s| format!("..{}", s))
             }
         }
@@ -1826,8 +1783,7 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
         } else {
             context.config.struct_lit_trailing_comma
         },
-        indent: indent,
-        width: budget,
+        shape: Shape::legacy(budget, indent),
         ends_with_newline: ends_with_newline,
         config: context.config,
     };
@@ -1870,27 +1826,23 @@ pub fn type_annotation_separator(config: &Config) -> &str {
     }
 }
 
-fn rewrite_field(context: &RewriteContext,
-                 field: &ast::Field,
-                 width: usize,
-                 offset: Indent)
-                 -> Option<String> {
+fn rewrite_field(context: &RewriteContext, field: &ast::Field, shape: Shape) -> Option<String> {
     let name = &field.ident.node.to_string();
     let separator = type_annotation_separator(context.config);
     let overhead = name.len() + separator.len();
     let expr = field.expr.rewrite(context,
-                                  try_opt!(width.checked_sub(overhead)),
-                                  offset + overhead);
+                                  Shape::legacy(try_opt!(shape.width.checked_sub(overhead)),
+                                                shape.indent + overhead));
 
     match expr {
         Some(e) => Some(format!("{}{}{}", name, separator, e)),
         None => {
-            let expr_offset = offset.block_indent(context.config);
+            let expr_offset = shape.indent.block_indent(context.config);
             let expr = field.expr.rewrite(context,
-                                          try_opt!(context.config
-                                              .max_width
-                                              .checked_sub(expr_offset.width())),
-                                          expr_offset);
+                                          Shape::legacy(try_opt!(context.config
+                                                            .max_width
+                                                            .checked_sub(expr_offset.width())),
+                                                        expr_offset));
             expr.map(|s| format!("{}:\n{}{}", name, expr_offset.to_string(&context.config), s))
         }
     }
@@ -1899,23 +1851,22 @@ fn rewrite_field(context: &RewriteContext,
 pub fn rewrite_tuple<'a, I>(context: &RewriteContext,
                             mut items: I,
                             span: Span,
-                            width: usize,
-                            offset: Indent)
+                            shape: Shape)
                             -> Option<String>
     where I: ExactSizeIterator,
           <I as Iterator>::Item: Deref,
           <I::Item as Deref>::Target: Rewrite + Spanned + 'a
 {
-    let indent = offset + 1;
+    let indent = shape.indent + 1;
     let aligned = RewriteContext { block_indent: indent, ..context.clone() };
 
     // In case of length 1, need a trailing comma
     if items.len() == 1 {
         // 3 = "(" + ",)"
-        let budget = try_opt!(width.checked_sub(3));
+        let budget = try_opt!(shape.width.checked_sub(3));
         return items.next()
             .unwrap()
-            .rewrite(&aligned, budget, indent)
+            .rewrite(&aligned, Shape::legacy(budget, indent))
             .map(|s| if context.config.spaces_within_parens {
                 format!("( {}, )", s)
             } else {
@@ -1924,16 +1875,16 @@ pub fn rewrite_tuple<'a, I>(context: &RewriteContext,
     }
 
     let list_lo = context.codemap.span_after(span, "(");
-    let budget = try_opt!(width.checked_sub(2));
+    let budget = try_opt!(shape.width.checked_sub(2));
     let items = itemize_list(context.codemap,
                              items,
                              ")",
                              |item| item.span().lo,
                              |item| item.span().hi,
-                             |item| item.rewrite(&aligned, budget, indent),
+                             |item| item.rewrite(&aligned, Shape::legacy(budget, indent)),
                              list_lo,
                              span.hi - BytePos(1));
-    let list_str = try_opt!(format_item_list(items, budget, indent, context.config));
+    let list_str = try_opt!(format_item_list(items, Shape::legacy(budget, indent), context.config));
 
     if context.config.spaces_within_parens && list_str.len() > 0 {
         Some(format!("( {} )", list_str))
@@ -1945,12 +1896,11 @@ pub fn rewrite_tuple<'a, I>(context: &RewriteContext,
 pub fn rewrite_unary_prefix<R: Rewrite>(context: &RewriteContext,
                                         prefix: &str,
                                         rewrite: &R,
-                                        width: usize,
-                                        offset: Indent)
+                                        shape: Shape)
                                         -> Option<String> {
     rewrite.rewrite(context,
-                 try_opt!(width.checked_sub(prefix.len())),
-                 offset + prefix.len())
+                 Shape::legacy(try_opt!(shape.width.checked_sub(prefix.len())),
+                               shape.indent + prefix.len()))
         .map(|r| format!("{}{}", prefix, r))
 }
 
@@ -1959,10 +1909,11 @@ pub fn rewrite_unary_prefix<R: Rewrite>(context: &RewriteContext,
 pub fn rewrite_unary_suffix<R: Rewrite>(context: &RewriteContext,
                                         suffix: &str,
                                         rewrite: &R,
-                                        width: usize,
-                                        offset: Indent)
+                                        shape: Shape)
                                         -> Option<String> {
-    rewrite.rewrite(context, try_opt!(width.checked_sub(suffix.len())), offset)
+    rewrite.rewrite(context,
+                 Shape::legacy(try_opt!(shape.width.checked_sub(suffix.len())),
+                               shape.indent))
         .map(|mut r| {
             r.push_str(suffix);
             r
@@ -1972,8 +1923,7 @@ pub fn rewrite_unary_suffix<R: Rewrite>(context: &RewriteContext,
 fn rewrite_unary_op(context: &RewriteContext,
                     op: &ast::UnOp,
                     expr: &ast::Expr,
-                    width: usize,
-                    offset: Indent)
+                    shape: Shape)
                     -> Option<String> {
     // For some reason, an UnOp is not spanned like BinOp!
     let operator_str = match *op {
@@ -1981,15 +1931,14 @@ fn rewrite_unary_op(context: &RewriteContext,
         ast::UnOp::Not => "!",
         ast::UnOp::Neg => "-",
     };
-    rewrite_unary_prefix(context, operator_str, expr, width, offset)
+    rewrite_unary_prefix(context, operator_str, expr, shape)
 }
 
 fn rewrite_assignment(context: &RewriteContext,
                       lhs: &ast::Expr,
                       rhs: &ast::Expr,
                       op: Option<&ast::BinOp>,
-                      width: usize,
-                      offset: Indent)
+                      shape: Shape)
                       -> Option<String> {
     let operator_str = match op {
         Some(op) => context.snippet(op.span),
@@ -1997,12 +1946,12 @@ fn rewrite_assignment(context: &RewriteContext,
     };
 
     // 1 = space between lhs and operator.
-    let max_width = try_opt!(width.checked_sub(operator_str.len() + 1));
+    let max_width = try_opt!(shape.width.checked_sub(operator_str.len() + 1));
     let lhs_str = format!("{} {}",
-                          try_opt!(lhs.rewrite(context, max_width, offset)),
+                          try_opt!(lhs.rewrite(context, Shape::legacy(max_width, shape.indent))),
                           operator_str);
 
-    rewrite_assign_rhs(context, lhs_str, rhs, width, offset)
+    rewrite_assign_rhs(context, lhs_str, rhs, shape)
 }
 
 // The left hand side must contain everything up to, and including, the
@@ -2010,19 +1959,19 @@ fn rewrite_assignment(context: &RewriteContext,
 pub fn rewrite_assign_rhs<S: Into<String>>(context: &RewriteContext,
                                            lhs: S,
                                            ex: &ast::Expr,
-                                           width: usize,
-                                           offset: Indent)
+                                           shape: Shape)
                                            -> Option<String> {
     let mut result = lhs.into();
     let last_line_width = last_line_width(&result) -
                           if result.contains('\n') {
-        offset.width()
+        shape.indent.width()
     } else {
         0
     };
     // 1 = space between operator and rhs.
-    let max_width = try_opt!(width.checked_sub(last_line_width + 1));
-    let rhs = ex.rewrite(context, max_width, offset + last_line_width + 1);
+    let max_width = try_opt!(shape.width.checked_sub(last_line_width + 1));
+    let rhs = ex.rewrite(context,
+                         Shape::legacy(max_width, shape.indent + last_line_width + 1));
 
     fn count_line_breaks(src: &str) -> usize {
         src.chars().filter(|&x| x == '\n').count()
@@ -2037,10 +1986,11 @@ pub fn rewrite_assign_rhs<S: Into<String>>(context: &RewriteContext,
             // Expression did not fit on the same line as the identifier or is
             // at least three lines big. Try splitting the line and see
             // if that works better.
-            let new_offset = offset.block_indent(context.config);
-            let max_width = try_opt!((width + offset.width()).checked_sub(new_offset.width()));
+            let new_offset = shape.indent.block_indent(context.config);
+            let max_width = try_opt!((shape.width + shape.indent.width())
+                .checked_sub(new_offset.width()));
             let inner_context = context.nested_context();
-            let new_rhs = ex.rewrite(&inner_context, max_width, new_offset);
+            let new_rhs = ex.rewrite(&inner_context, Shape::legacy(max_width, new_offset));
 
             // FIXME: DRY!
             match (rhs, new_rhs) {
@@ -2069,12 +2019,11 @@ pub fn rewrite_assign_rhs<S: Into<String>>(context: &RewriteContext,
 fn rewrite_expr_addrof(context: &RewriteContext,
                        mutability: ast::Mutability,
                        expr: &ast::Expr,
-                       width: usize,
-                       offset: Indent)
+                       shape: Shape)
                        -> Option<String> {
     let operator_str = match mutability {
         ast::Mutability::Immutable => "&",
         ast::Mutability::Mutable => "&mut ",
     };
-    rewrite_unary_prefix(context, operator_str, expr, width, offset)
+    rewrite_unary_prefix(context, operator_str, expr, shape)
 }
diff --git a/src/imports.rs b/src/imports.rs
index a75a571c553..6e7c2d0f7e2 100644
--- a/src/imports.rs
+++ b/src/imports.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use Indent;
+use Shape;
 use utils;
 use syntax::codemap::{self, BytePos, Span};
 use codemap::SpanUtils;
@@ -125,19 +125,19 @@ fn compare_use_items(a: &ast::Item, b: &ast::Item) -> Option<Ordering> {
 
 impl Rewrite for ast::ViewPath {
     // Returns an empty string when the ViewPath is empty (like foo::bar::{})
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
         match self.node {
             ast::ViewPath_::ViewPathList(_, ref path_list) if path_list.is_empty() => {
                 Some(String::new())
             }
             ast::ViewPath_::ViewPathList(ref path, ref path_list) => {
-                rewrite_use_list(width, offset, path, path_list, self.span, context)
+                rewrite_use_list(shape, path, path_list, self.span, context)
             }
             ast::ViewPath_::ViewPathGlob(_) => None,
             ast::ViewPath_::ViewPathSimple(ident, ref path) => {
                 let ident_str = ident.to_string();
                 // 4 = " as ".len()
-                let budget = try_opt!(width.checked_sub(ident_str.len() + 4));
+                let budget = try_opt!(shape.width.checked_sub(ident_str.len() + 4));
 
                 let path_str = if path.segments.last().unwrap().identifier.to_string() == "self" &&
                                   path.segments.len() > 1 {
@@ -149,10 +149,13 @@ impl Rewrite for ast::ViewPath {
                                           PathContext::Import,
                                           None,
                                           &path,
-                                          budget,
-                                          offset))
+                                          Shape::legacy(budget, shape.indent)))
                 } else {
-                    try_opt!(rewrite_path(context, PathContext::Import, None, path, budget, offset))
+                    try_opt!(rewrite_path(context,
+                                          PathContext::Import,
+                                          None,
+                                          path,
+                                          Shape::legacy(budget, shape.indent)))
                 };
 
                 Some(if path.segments.last().unwrap().identifier == ident {
@@ -225,8 +228,7 @@ impl<'a> FmtVisitor<'a> {
         offset.alignment += vis.len() + "use ".len();
         // 1 = ";"
         match vp.rewrite(&self.get_context(),
-                         self.config.max_width - offset.width() - 1,
-                         offset) {
+                         Shape::legacy(self.config.max_width - offset.width() - 1, offset)) {
             Some(ref s) if s.is_empty() => {
                 // Format up to last newline
                 let prev_span = codemap::mk_sp(self.last_pos, source!(self, span).lo);
@@ -283,15 +285,14 @@ fn append_alias(path_item_str: String, vpi: &ast::PathListItem) -> String {
 
 // Pretty prints a multi-item import.
 // Assumes that path_list.len() > 0.
-pub fn rewrite_use_list(width: usize,
-                        offset: Indent,
+pub fn rewrite_use_list(shape: Shape,
                         path: &ast::Path,
                         path_list: &[ast::PathListItem],
                         span: Span,
                         context: &RewriteContext)
                         -> Option<String> {
     // Returns a different option to distinguish `::foo` and `foo`
-    let path_str = try_opt!(rewrite_path(context, PathContext::Import, None, path, width, offset));
+    let path_str = try_opt!(rewrite_path(context, PathContext::Import, None, path, shape));
 
     match path_list.len() {
         0 => unreachable!(),
@@ -300,7 +301,7 @@ pub fn rewrite_use_list(width: usize,
     }
 
     // 2 = {}
-    let remaining_width = width.checked_sub(path_str.len() + 2).unwrap_or(0);
+    let remaining_width = shape.width.checked_sub(path_str.len() + 2).unwrap_or(0);
 
     let mut items = {
         // Dummy value, see explanation below.
@@ -336,11 +337,11 @@ pub fn rewrite_use_list(width: usize,
         tactic: tactic,
         separator: ",",
         trailing_separator: SeparatorTactic::Never,
-        indent: offset + path_str.len() + 1 + colons_offset,
         // FIXME This is too conservative, and will not use all width
         // available
         // (loose 1 column (";"))
-        width: remaining_width,
+        shape: Shape::legacy(remaining_width,
+                             shape.indent + path_str.len() + 1 + colons_offset),
         ends_with_newline: false,
         config: context.config,
     };
diff --git a/src/items.rs b/src/items.rs
index 8270a52ce76..3c5bf3ce6ce 100644
--- a/src/items.rs
+++ b/src/items.rs
@@ -10,7 +10,7 @@
 
 // Formatting top-level items - functions, structs, enums, traits, impls.
 
-use Indent;
+use {Indent, Shape};
 use codemap::SpanUtils;
 use utils::{format_mutability, format_visibility, contains_skip, end_typaram, wrap_str,
             last_line_width, format_unsafety, trim_newlines, stmt_expr, semicolon_for_expr};
@@ -30,14 +30,18 @@ use syntax::ast::ImplItem;
 // Statements of the form
 // let pat: ty = init;
 impl Rewrite for ast::Local {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
-        debug!("Local::rewrite {:?} {} {:?}", self, width, offset);
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
+        debug!("Local::rewrite {:?} {} {:?}",
+               self,
+               shape.width,
+               shape.indent);
         let mut result = "let ".to_owned();
-        let pattern_offset = offset + result.len();
+        let pattern_offset = shape.indent + result.len();
         // 1 = ;
-        let pattern_width = try_opt!(width.checked_sub(pattern_offset.width() + 1));
+        let pattern_width = try_opt!(shape.width.checked_sub(pattern_offset.width() + 1));
 
-        let pat_str = try_opt!(self.pat.rewrite(&context, pattern_width, pattern_offset));
+        let pat_str = try_opt!(self.pat
+            .rewrite(&context, Shape::legacy(pattern_width, pattern_offset)));
         result.push_str(&pat_str);
 
         // String that is placed within the assignment pattern and expression.
@@ -46,10 +50,10 @@ impl Rewrite for ast::Local {
 
             if let Some(ref ty) = self.ty {
                 let separator = type_annotation_separator(context.config);
-                let indent = offset + last_line_width(&result) + separator.len();
+                let indent = shape.indent + last_line_width(&result) + separator.len();
                 // 1 = ;
-                let budget = try_opt!(width.checked_sub(indent.width() + 1));
-                let rewrite = try_opt!(ty.rewrite(context, budget, indent));
+                let budget = try_opt!(shape.width.checked_sub(indent.width() + 1));
+                let rewrite = try_opt!(ty.rewrite(context, Shape::legacy(budget, indent)));
 
                 infix.push_str(separator);
                 infix.push_str(&rewrite);
@@ -66,10 +70,12 @@ impl Rewrite for ast::Local {
 
         if let Some(ref ex) = self.init {
             // 1 = trailing semicolon;
-            let budget = try_opt!(width.checked_sub(context.block_indent.width() + 1));
+            let budget = try_opt!(shape.width.checked_sub(context.block_indent.width() + 1));
 
-            result =
-                try_opt!(rewrite_assign_rhs(&context, result, ex, budget, context.block_indent));
+            result = try_opt!(rewrite_assign_rhs(&context,
+                                                 result,
+                                                 ex,
+                                                 Shape::legacy(budget, context.block_indent)));
         }
 
         result.push(';');
@@ -200,7 +206,7 @@ impl<'a> FmtVisitor<'a> {
                 let offset = self.block_indent + prefix.len();
                 // 1 = ;
                 let width = self.config.max_width - offset.width() - 1;
-                let rewrite = ty.rewrite(&self.get_context(), width, offset);
+                let rewrite = ty.rewrite(&self.get_context(), Shape::legacy(width, offset));
 
                 match rewrite {
                     Some(result) => {
@@ -321,15 +327,17 @@ impl<'a> FmtVisitor<'a> {
                             let suffix = if semicolon_for_expr(e) { ";" } else { "" };
 
                             e.rewrite(&self.get_context(),
-                                         self.config.max_width - self.block_indent.width(),
-                                         self.block_indent)
+                                         Shape::legacy(self.config.max_width -
+                                                       self.block_indent.width(),
+                                                       self.block_indent))
                                 .map(|s| s + suffix)
                                 .or_else(|| Some(self.snippet(e.span)))
                         }
                         None => {
                             stmt.rewrite(&self.get_context(),
-                                         self.config.max_width - self.block_indent.width(),
-                                         self.block_indent)
+                                         Shape::legacy(self.config.max_width -
+                                                       self.block_indent.width(),
+                                                       self.block_indent))
                         }
                     }
                 } else {
@@ -426,8 +434,7 @@ impl<'a> FmtVisitor<'a> {
             tactic: DefinitiveListTactic::Vertical,
             separator: ",",
             trailing_separator: SeparatorTactic::from_bool(self.config.enum_trailing_comma),
-            indent: self.block_indent,
-            width: budget,
+            shape: Shape::legacy(budget, self.block_indent),
             ends_with_newline: true,
             config: self.config,
         };
@@ -450,8 +457,7 @@ impl<'a> FmtVisitor<'a> {
         let mut result = try_opt!(field.node
             .attrs
             .rewrite(&self.get_context(),
-                     self.config.max_width - indent.width(),
-                     indent));
+                     Shape::legacy(self.config.max_width - indent.width(), indent)));
         if !result.is_empty() {
             result.push('\n');
             result.push_str(&indent.to_string(self.config));
@@ -481,8 +487,7 @@ impl<'a> FmtVisitor<'a> {
 
                 wrap_str(tag,
                          self.config.max_width,
-                         self.config.max_width - indent.width(),
-                         indent)
+                         Shape::legacy(self.config.max_width - indent.width(), indent))
             }
         };
 
@@ -514,8 +519,8 @@ pub fn format_impl(context: &RewriteContext, item: &ast::Item, offset: Indent) -
                                                              &generics.where_clause,
                                                              context.config,
                                                              context.config.item_brace_style,
-                                                             context.block_indent,
-                                                             where_budget,
+                                                             Shape::legacy(where_budget,
+                                                                           context.block_indent),
                                                              context.config.where_density,
                                                              "{",
                                                              true,
@@ -628,8 +633,8 @@ fn format_impl_ref_and_type(context: &RewriteContext,
         };
         let generics_str = try_opt!(rewrite_generics(context,
                                                      generics,
-                                                     offset,
-                                                     context.config.max_width,
+                                                     Shape::legacy(context.config.max_width,
+                                                                   offset),
                                                      offset + result.len(),
                                                      mk_sp(lo, hi)));
         result.push_str(&generics_str);
@@ -643,7 +648,7 @@ fn format_impl_ref_and_type(context: &RewriteContext,
             }
             let budget = try_opt!(context.config.max_width.checked_sub(result.len()));
             let indent = offset + result.len();
-            result.push_str(&*try_opt!(trait_ref.rewrite(context, budget, indent)));
+            result.push_str(&*try_opt!(trait_ref.rewrite(context, Shape::legacy(budget, indent))));
 
             if split_at_for {
                 result.push('\n');
@@ -673,7 +678,7 @@ fn format_impl_ref_and_type(context: &RewriteContext,
         // 1 = space before the type.
         let budget = try_opt!(context.config.max_width.checked_sub(used_space + 1));
         let indent = offset + result.len() + 1;
-        let self_ty_str = self_ty.rewrite(context, budget, indent);
+        let self_ty_str = self_ty.rewrite(context, Shape::legacy(budget, indent));
         if let Some(self_ty_str) = self_ty_str {
             result.push_str(" ");
             result.push_str(&self_ty_str);
@@ -684,7 +689,7 @@ fn format_impl_ref_and_type(context: &RewriteContext,
         let indent = offset.block_indent(context.config);
         result.push_str(&format!("\n{}", indent.to_string(context.config)));
         let budget = try_opt!(context.config.max_width.checked_sub(indent.width()));
-        result.push_str(&*try_opt!(self_ty.rewrite(context, budget, indent)));
+        result.push_str(&*try_opt!(self_ty.rewrite(context, Shape::legacy(budget, indent))));
         Some(result)
     } else {
         unreachable!();
@@ -742,16 +747,16 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent)
 
         let generics_str = try_opt!(rewrite_generics(context,
                                                      generics,
-                                                     offset,
-                                                     context.config.max_width,
+                                                     Shape::legacy(context.config.max_width,
+                                                                   offset),
                                                      offset + result.len(),
                                                      mk_sp(item.span.lo, body_lo)));
         result.push_str(&generics_str);
 
-        let trait_bound_str = try_opt!(rewrite_trait_bounds(context,
-                                                            type_param_bounds,
-                                                            offset,
-                                                            context.config.max_width));
+        let trait_bound_str =
+            try_opt!(rewrite_trait_bounds(context,
+                                          type_param_bounds,
+                                          Shape::legacy(context.config.max_width, offset)));
         // If the trait, generics, and trait bound cannot fit on the same line,
         // put the trait bounds on an indented new line
         if offset.width() + last_line_width(&result) + trait_bound_str.len() >
@@ -783,8 +788,8 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent)
                                                              &generics.where_clause,
                                                              context.config,
                                                              context.config.item_brace_style,
-                                                             context.block_indent,
-                                                             where_budget,
+                                                             Shape::legacy(where_budget,
+                                                                           context.block_indent),
                                                              where_density,
                                                              "{",
                                                              has_body,
@@ -916,22 +921,23 @@ fn format_struct_struct(context: &RewriteContext,
     // 1 = ","
     let item_budget = try_opt!(context.config.max_width.checked_sub(item_indent.width() + 1));
 
-    let items = itemize_list(context.codemap,
-                             fields.iter(),
-                             "}",
-                             |field| {
-        // Include attributes and doc comments, if present
-        if !field.attrs.is_empty() {
-            field.attrs[0].span.lo
-        } else {
-            field.span.lo
-        }
-    },
-                             |field| field.ty.span.hi,
-                             |field| field.rewrite(context, item_budget, item_indent),
-                             context.codemap.span_after(span, "{"),
-                             span.hi)
-        .collect::<Vec<_>>();
+    let items =
+        itemize_list(context.codemap,
+                     fields.iter(),
+                     "}",
+                     |field| {
+            // Include attributes and doc comments, if present
+            if !field.attrs.is_empty() {
+                field.attrs[0].span.lo
+            } else {
+                field.span.lo
+            }
+        },
+                     |field| field.ty.span.hi,
+                     |field| field.rewrite(context, Shape::legacy(item_budget, item_indent)),
+                     context.codemap.span_after(span, "{"),
+                     span.hi)
+            .collect::<Vec<_>>();
     // 1 = ,
     let budget = context.config.max_width - offset.width() + context.config.tab_spaces - 1;
 
@@ -944,8 +950,7 @@ fn format_struct_struct(context: &RewriteContext,
         tactic: tactic,
         separator: ",",
         trailing_separator: context.config.struct_trailing_comma,
-        indent: item_indent,
-        width: budget,
+        shape: Shape::legacy(budget, item_indent),
         ends_with_newline: true,
         config: context.config,
     };
@@ -986,8 +991,8 @@ fn format_tuple_struct(context: &RewriteContext,
         Some(generics) => {
             let generics_str = try_opt!(rewrite_generics(context,
                                                          generics,
-                                                         offset,
-                                                         context.config.max_width,
+                                                         Shape::legacy(context.config.max_width,
+                                                                       offset),
                                                          offset + header_str.len(),
                                                          mk_sp(span.lo, body_lo)));
             result.push_str(&generics_str);
@@ -999,8 +1004,7 @@ fn format_tuple_struct(context: &RewriteContext,
                                           &generics.where_clause,
                                           context.config,
                                           context.config.item_brace_style,
-                                          context.block_indent,
-                                          where_budget,
+                                          Shape::legacy(where_budget, context.block_indent),
                                           Density::Compressed,
                                           ";",
                                           false,
@@ -1014,22 +1018,25 @@ fn format_tuple_struct(context: &RewriteContext,
     // 2 = ");"
     let item_budget = try_opt!(context.config.max_width.checked_sub(item_indent.width() + 2));
 
-    let items = itemize_list(context.codemap,
-                             fields.iter(),
-                             ")",
-                             |field| {
-        // Include attributes and doc comments, if present
-        if !field.attrs.is_empty() {
-            field.attrs[0].span.lo
-        } else {
-            field.span.lo
-        }
-    },
-                             |field| field.ty.span.hi,
-                             |field| field.rewrite(context, item_budget, item_indent),
-                             context.codemap.span_after(span, "("),
-                             span.hi);
-    let body = try_opt!(format_item_list(items, item_budget, item_indent, context.config));
+    let items =
+        itemize_list(context.codemap,
+                     fields.iter(),
+                     ")",
+                     |field| {
+            // Include attributes and doc comments, if present
+            if !field.attrs.is_empty() {
+                field.attrs[0].span.lo
+            } else {
+                field.span.lo
+            }
+        },
+                     |field| field.ty.span.hi,
+                     |field| field.rewrite(context, Shape::legacy(item_budget, item_indent)),
+                     context.codemap.span_after(span, "("),
+                     span.hi);
+    let body = try_opt!(format_item_list(items,
+                                         Shape::legacy(item_budget, item_indent),
+                                         context.config));
 
     if context.config.spaces_within_parens && body.len() > 0 {
         result.push(' ');
@@ -1077,8 +1084,7 @@ pub fn rewrite_type_alias(context: &RewriteContext,
     let generics_width = context.config.max_width - " =".len();
     let generics_str = try_opt!(rewrite_generics(context,
                                                  generics,
-                                                 indent,
-                                                 generics_width,
+                                                 Shape::legacy(generics_width, indent),
                                                  generics_indent,
                                                  generics_span));
 
@@ -1091,8 +1097,7 @@ pub fn rewrite_type_alias(context: &RewriteContext,
                                                          &generics.where_clause,
                                                          context.config,
                                                          context.config.item_brace_style,
-                                                         indent,
-                                                         where_budget,
+                                                         Shape::legacy(where_budget, indent),
                                                          context.config.where_density,
                                                          "=",
                                                          false,
@@ -1109,7 +1114,7 @@ pub fn rewrite_type_alias(context: &RewriteContext,
         .unwrap_or(0);
     let type_indent = indent + line_width;
     // Try to fit the type on the same line
-    let ty_str = try_opt!(ty.rewrite(context, budget, type_indent)
+    let ty_str = try_opt!(ty.rewrite(context, Shape::legacy(budget, type_indent))
         .or_else(|| {
             // The line was too short, try to put the type on the next line
 
@@ -1121,7 +1126,7 @@ pub fn rewrite_type_alias(context: &RewriteContext,
             let budget = try_opt!(context.config
                 .max_width
                 .checked_sub(type_indent.width() + ";".len()));
-            ty.rewrite(context, budget, type_indent)
+            ty.rewrite(context, Shape::legacy(budget, type_indent))
         }));
     result.push_str(&ty_str);
     result.push_str(";");
@@ -1142,19 +1147,21 @@ fn type_annotation_spacing(config: &Config) -> (&str, &str) {
 }
 
 impl Rewrite for ast::StructField {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
         if contains_skip(&self.attrs) {
             let span = context.snippet(mk_sp(self.attrs[0].span.lo, self.span.hi));
-            return wrap_str(span, context.config.max_width, width, offset);
+            return wrap_str(span, context.config.max_width, shape);
         }
 
         let name = self.ident;
         let vis = format_visibility(&self.vis);
         let mut attr_str = try_opt!(self.attrs
-            .rewrite(context, context.config.max_width - offset.width(), offset));
+            .rewrite(context,
+                     Shape::legacy(context.config.max_width - shape.indent.width(),
+                                   shape.indent)));
         if !attr_str.is_empty() {
             attr_str.push('\n');
-            attr_str.push_str(&offset.to_string(context.config));
+            attr_str.push_str(&shape.indent.to_string(context.config));
         }
 
         let type_annotation_spacing = type_annotation_spacing(context.config);
@@ -1171,8 +1178,10 @@ impl Rewrite for ast::StructField {
         };
 
         let last_line_width = last_line_width(&result);
-        let budget = try_opt!(width.checked_sub(last_line_width));
-        let rewrite = try_opt!(self.ty.rewrite(context, budget, offset + last_line_width));
+        let budget = try_opt!(shape.width.checked_sub(last_line_width));
+        let rewrite = try_opt!(self.ty.rewrite(context,
+                                               Shape::legacy(budget,
+                                                             shape.indent + last_line_width)));
         Some(result + &rewrite)
     }
 }
@@ -1195,15 +1204,20 @@ pub fn rewrite_static(prefix: &str,
                          type_annotation_spacing.1);
     // 2 = " =".len()
     let ty_str = try_opt!(ty.rewrite(context,
-                                     context.config.max_width - context.block_indent.width() -
-                                     prefix.len() - 2,
-                                     context.block_indent));
+                                     Shape::legacy(context.config.max_width -
+                                                   context.block_indent.width() -
+                                                   prefix.len() -
+                                                   2,
+                                                   context.block_indent)));
 
     if let Some(expr) = expr_opt {
         let lhs = format!("{}{} =", prefix, ty_str);
         // 1 = ;
         let remaining_width = context.config.max_width - context.block_indent.width() - 1;
-        rewrite_assign_rhs(context, lhs, expr, remaining_width, context.block_indent)
+        rewrite_assign_rhs(context,
+                           lhs,
+                           expr,
+                           Shape::legacy(remaining_width, context.block_indent))
             .map(|s| s + ";")
     } else {
         let lhs = format!("{}{};", prefix, ty_str);
@@ -1222,7 +1236,9 @@ pub fn rewrite_associated_type(ident: ast::Ident,
     let type_bounds_str = if let Some(ty_param_bounds) = ty_param_bounds_opt {
         let bounds: &[_] = ty_param_bounds;
         let bound_str = try_opt!(bounds.iter()
-            .map(|ty_bound| ty_bound.rewrite(context, context.config.max_width, indent))
+            .map(|ty_bound| {
+                ty_bound.rewrite(context, Shape::legacy(context.config.max_width, indent))
+            })
             .intersperse(Some(" + ".to_string()))
             .collect::<Option<String>>());
         if bounds.len() > 0 {
@@ -1236,10 +1252,11 @@ pub fn rewrite_associated_type(ident: ast::Ident,
 
     if let Some(ty) = ty_opt {
         let ty_str = try_opt!(ty.rewrite(context,
-                                         context.config.max_width - context.block_indent.width() -
-                                         prefix.len() -
-                                         2,
-                                         context.block_indent));
+                                         Shape::legacy(context.config.max_width -
+                                                       context.block_indent.width() -
+                                                       prefix.len() -
+                                                       2,
+                                                       context.block_indent)));
         Some(format!("{} = {};", prefix, ty_str))
     } else {
         Some(format!("{}{};", prefix, type_bounds_str))
@@ -1247,21 +1264,23 @@ pub fn rewrite_associated_type(ident: ast::Ident,
 }
 
 impl Rewrite for ast::FunctionRetTy {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
         match *self {
             ast::FunctionRetTy::Default(_) => Some(String::new()),
             ast::FunctionRetTy::Ty(ref ty) => {
-                let inner_width = try_opt!(width.checked_sub(3));
-                ty.rewrite(context, inner_width, offset + 3).map(|r| format!("-> {}", r))
+                let inner_width = try_opt!(shape.width.checked_sub(3));
+                ty.rewrite(context, Shape::legacy(inner_width, shape.indent + 3))
+                    .map(|r| format!("-> {}", r))
             }
         }
     }
 }
 
 impl Rewrite for ast::Arg {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
         if is_named_arg(self) {
-            let mut result = try_opt!(self.pat.rewrite(context, width, offset));
+            let mut result = try_opt!(self.pat
+                .rewrite(context, Shape::legacy(shape.width, shape.indent)));
 
             if self.ty.node != ast::TyKind::Infer {
                 if context.config.space_before_type_annotation {
@@ -1271,14 +1290,16 @@ impl Rewrite for ast::Arg {
                 if context.config.space_after_type_annotation_colon {
                     result.push_str(" ");
                 }
-                let max_width = try_opt!(width.checked_sub(result.len()));
-                let ty_str = try_opt!(self.ty.rewrite(context, max_width, offset + result.len()));
+                let max_width = try_opt!(shape.width.checked_sub(result.len()));
+                let ty_str = try_opt!(self.ty.rewrite(context,
+                                                      Shape::legacy(max_width,
+                                                                    shape.indent + result.len())));
                 result.push_str(&ty_str);
             }
 
             Some(result)
         } else {
-            self.ty.rewrite(context, width, offset)
+            self.ty.rewrite(context, shape)
         }
     }
 }
@@ -1292,8 +1313,8 @@ fn rewrite_explicit_self(explicit_self: &ast::ExplicitSelf,
             let mut_str = format_mutability(m);
             match lt {
                 Some(ref l) => {
-                    let lifetime_str =
-                        try_opt!(l.rewrite(context, usize::max_value(), Indent::empty()));
+                    let lifetime_str = try_opt!(l.rewrite(context,
+                                           Shape::legacy(usize::max_value(), Indent::empty())));
                     Some(format!("&{} {}self", lifetime_str, mut_str))
                 }
                 None => Some(format!("&{}self", mut_str)),
@@ -1303,7 +1324,8 @@ fn rewrite_explicit_self(explicit_self: &ast::ExplicitSelf,
             assert!(!args.is_empty(), "&[ast::Arg] shouldn't be empty.");
 
             let mutability = explicit_self_mutability(&args[0]);
-            let type_str = try_opt!(ty.rewrite(context, usize::max_value(), Indent::empty()));
+            let type_str =
+                try_opt!(ty.rewrite(context, Shape::legacy(usize::max_value(), Indent::empty())));
 
             Some(format!("{}self: {}", format_mutability(mutability), type_str))
         }
@@ -1427,8 +1449,7 @@ fn rewrite_fn_base(context: &RewriteContext,
     let generics_span = mk_sp(span.lo, span_for_return(&fd.output).lo);
     let generics_str = try_opt!(rewrite_generics(context,
                                                  generics,
-                                                 indent,
-                                                 context.config.max_width,
+                                                 Shape::legacy(context.config.max_width, indent),
                                                  generics_indent,
                                                  generics_span));
     result.push_str(&generics_str);
@@ -1436,7 +1457,8 @@ fn rewrite_fn_base(context: &RewriteContext,
     // Note that if the width and indent really matter, we'll re-layout the
     // return type later anyway.
     let ret_str = try_opt!(fd.output
-        .rewrite(&context, context.config.max_width - indent.width(), indent));
+        .rewrite(&context,
+                 Shape::legacy(context.config.max_width - indent.width(), indent)));
 
     let multi_line_ret_str = ret_str.contains('\n');
     let ret_str_len = if multi_line_ret_str { 0 } else { ret_str.len() };
@@ -1571,7 +1593,7 @@ fn rewrite_fn_base(context: &RewriteContext,
             // Now that we know the proper indent and width, we need to
             // re-layout the return type.
             let budget = try_opt!(context.config.max_width.checked_sub(ret_indent.width()));
-            let ret_str = try_opt!(fd.output.rewrite(context, budget, ret_indent));
+            let ret_str = try_opt!(fd.output.rewrite(context, Shape::legacy(budget, ret_indent)));
             result.push_str(&ret_str);
         } else {
             result.push_str(&ret_str);
@@ -1612,8 +1634,7 @@ fn rewrite_fn_base(context: &RewriteContext,
                                                          where_clause,
                                                          context.config,
                                                          context.config.fn_brace_style,
-                                                         indent,
-                                                         where_budget,
+                                                         Shape::legacy(where_budget, indent),
                                                          where_density,
                                                          "{",
                                                          has_body,
@@ -1640,7 +1661,7 @@ fn rewrite_args(context: &RewriteContext,
                 variadic: bool)
                 -> Option<String> {
     let mut arg_item_strs = try_opt!(args.iter()
-        .map(|arg| arg.rewrite(&context, multi_line_budget, arg_indent))
+        .map(|arg| arg.rewrite(&context, Shape::legacy(multi_line_budget, arg_indent)))
         .collect::<Option<Vec<_>>>());
 
     // Account for sugary self.
@@ -1744,8 +1765,7 @@ fn rewrite_args(context: &RewriteContext,
         tactic: tactic,
         separator: ",",
         trailing_separator: SeparatorTactic::Never,
-        indent: indent,
-        width: budget,
+        shape: Shape::legacy(budget, indent),
         ends_with_newline: end_with_newline,
         config: context.config,
     };
@@ -1812,8 +1832,8 @@ fn newline_for_brace(config: &Config, where_clause: &ast::WhereClause) -> bool {
 
 fn rewrite_generics(context: &RewriteContext,
                     generics: &ast::Generics,
-                    offset: Indent,
-                    width: usize,
+                    shape: Shape,
+                    // TODO shouldn't need this
                     generics_offset: Indent,
                     span: Span)
                     -> Option<String> {
@@ -1826,18 +1846,19 @@ fn rewrite_generics(context: &RewriteContext,
     }
 
     let offset = match context.config.generics_indent {
-        BlockIndentStyle::Inherit => offset,
-        BlockIndentStyle::Tabbed => offset.block_indent(context.config),
+        BlockIndentStyle::Inherit => shape.indent,
+        BlockIndentStyle::Tabbed => shape.indent.block_indent(context.config),
         // 1 = <
         BlockIndentStyle::Visual => generics_offset + 1,
     };
 
-    let h_budget = try_opt!(width.checked_sub(generics_offset.width() + 2));
+    let h_budget = try_opt!(shape.width.checked_sub(generics_offset.width() + 2));
     // FIXME: might need to insert a newline if the generics are really long.
 
     // Strings for the generics.
-    let lt_strs = lifetimes.iter().map(|lt| lt.rewrite(context, h_budget, offset));
-    let ty_strs = tys.iter().map(|ty_param| ty_param.rewrite(context, h_budget, offset));
+    let lt_strs = lifetimes.iter().map(|lt| lt.rewrite(context, Shape::legacy(h_budget, offset)));
+    let ty_strs = tys.iter()
+        .map(|ty_param| ty_param.rewrite(context, Shape::legacy(h_budget, offset)));
 
     // Extract comments between generics.
     let lt_spans = lifetimes.iter().map(|l| {
@@ -1859,7 +1880,8 @@ fn rewrite_generics(context: &RewriteContext,
                              |&(_, ref str)| str.clone(),
                              context.codemap.span_after(span, "<"),
                              span.hi);
-    let list_str = try_opt!(format_item_list(items, h_budget, offset, context.config));
+    let list_str =
+        try_opt!(format_item_list(items, Shape::legacy(h_budget, offset), context.config));
 
     Some(if context.config.spaces_within_angle_brackets {
         format!("< {} >", list_str)
@@ -1870,8 +1892,7 @@ fn rewrite_generics(context: &RewriteContext,
 
 fn rewrite_trait_bounds(context: &RewriteContext,
                         type_param_bounds: &ast::TyParamBounds,
-                        indent: Indent,
-                        width: usize)
+                        shape: Shape)
                         -> Option<String> {
     let bounds: &[_] = type_param_bounds;
 
@@ -1880,7 +1901,7 @@ fn rewrite_trait_bounds(context: &RewriteContext,
     }
 
     let bound_str = try_opt!(bounds.iter()
-        .map(|ty_bound| ty_bound.rewrite(&context, width, indent))
+        .map(|ty_bound| ty_bound.rewrite(&context, shape))
         .intersperse(Some(" + ".to_string()))
         .collect::<Option<String>>());
 
@@ -1894,8 +1915,7 @@ fn rewrite_where_clause(context: &RewriteContext,
                         where_clause: &ast::WhereClause,
                         config: &Config,
                         brace_style: BraceStyle,
-                        indent: Indent,
-                        width: usize,
+                        shape: Shape,
                         density: Density,
                         terminator: &str,
                         allow_trailing_comma: bool,
@@ -1911,10 +1931,10 @@ fn rewrite_where_clause(context: &RewriteContext,
     };
 
     let offset = match context.config.where_pred_indent {
-        BlockIndentStyle::Inherit => indent + extra_indent,
-        BlockIndentStyle::Tabbed => indent + extra_indent.block_indent(config),
+        BlockIndentStyle::Inherit => shape.indent + extra_indent,
+        BlockIndentStyle::Tabbed => shape.indent + extra_indent.block_indent(config),
         // 6 = "where ".len()
-        BlockIndentStyle::Visual => indent + extra_indent + 6,
+        BlockIndentStyle::Visual => shape.indent + extra_indent + 6,
     };
     // FIXME: if where_pred_indent != Visual, then the budgets below might
     // be out by a char or two.
@@ -1931,7 +1951,7 @@ fn rewrite_where_clause(context: &RewriteContext,
                              terminator,
                              |pred| span_for_where_pred(pred).lo,
                              |pred| span_for_where_pred(pred).hi,
-                             |pred| pred.rewrite(context, budget, offset),
+                             |pred| pred.rewrite(context, Shape::legacy(budget, offset)),
                              span_start,
                              span_end);
     let item_vec = items.collect::<Vec<_>>();
@@ -1944,8 +1964,7 @@ fn rewrite_where_clause(context: &RewriteContext,
         tactic: tactic,
         separator: ",",
         trailing_separator: SeparatorTactic::from_bool(use_trailing_comma),
-        indent: offset,
-        width: budget,
+        shape: Shape::legacy(budget, offset),
         ends_with_newline: true,
         config: context.config,
     };
@@ -1965,9 +1984,9 @@ fn rewrite_where_clause(context: &RewriteContext,
         terminator.len()
     };
     if density == Density::Tall || preds_str.contains('\n') ||
-       indent.width() + " where ".len() + preds_str.len() + end_length > width {
+       shape.indent.width() + " where ".len() + preds_str.len() + end_length > shape.width {
         Some(format!("\n{}where {}",
-                     (indent + extra_indent).to_string(context.config),
+                     (shape.indent + extra_indent).to_string(context.config),
                      preds_str))
     } else {
         Some(format!(" where {}", preds_str))
@@ -1990,8 +2009,7 @@ fn format_generics(context: &RewriteContext,
                    -> Option<String> {
     let mut result = try_opt!(rewrite_generics(context,
                                                generics,
-                                               offset,
-                                               context.config.max_width,
+                                               Shape::legacy(context.config.max_width, offset),
                                                generics_offset,
                                                span));
 
@@ -2001,8 +2019,8 @@ fn format_generics(context: &RewriteContext,
                                                              &generics.where_clause,
                                                              context.config,
                                                              brace_style,
-                                                             context.block_indent,
-                                                             budget,
+                                                             Shape::legacy(budget,
+                                                                           context.block_indent),
                                                              Density::Tall,
                                                              terminator,
                                                              true,
diff --git a/src/lib.rs b/src/lib.rs
index 9e80e8f1b6b..3caeaf92ebb 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -201,6 +201,50 @@ impl Sub<usize> for Indent {
     }
 }
 
+#[derive(Copy, Clone, Debug)]
+pub struct Shape {
+    pub width: usize,
+    pub indent: Indent,
+}
+
+impl Shape {
+    pub fn indented(indent: Indent, config: &Config) -> Shape {
+        Shape {
+            width: config.max_width,
+            indent: indent,
+        }
+    }
+
+    /// `indent` is the indentation of the first line. The next lines
+    /// should begin with at least `indent` spaces (except backwards
+    /// indentation). The first line should not begin with indentation.
+    /// `width` is the maximum number of characters on the last line
+    /// (excluding `indent`). The width of other lines is not limited by
+    /// `width`.
+    /// Note that in reality, we sometimes use width for lines other than the
+    /// last (i.e., we are conservative).
+    // .......*-------*
+    //        |       |
+    //        |     *-*
+    //        *-----|
+    // |<------------>|  max width
+    // |<---->|          indent
+    //        |<--->|    width
+    pub fn legacy(width: usize, indent: Indent) -> Shape {
+        Shape {
+            width: width,
+            indent: indent,
+        }
+    }
+
+    pub fn sub_width(self, width: usize) -> Shape {
+        Shape {
+            width: self.width - width,
+            indent: self.indent,
+        }
+    }
+}
+
 pub enum ErrorKind {
     // Line has exceeded character limit (found, maximum)
     LineOverflow(usize, usize),
diff --git a/src/lists.rs b/src/lists.rs
index 9af4fc238d6..2c78a1040c8 100644
--- a/src/lists.rs
+++ b/src/lists.rs
@@ -13,7 +13,7 @@ use std::iter::Peekable;
 
 use syntax::codemap::{self, CodeMap, BytePos};
 
-use Indent;
+use {Indent, Shape};
 use comment::{FindUncommented, rewrite_comment, find_comment_end};
 use config::Config;
 
@@ -59,50 +59,38 @@ pub struct ListFormatting<'a> {
     pub tactic: DefinitiveListTactic,
     pub separator: &'a str,
     pub trailing_separator: SeparatorTactic,
-    pub indent: Indent,
-    pub width: usize,
+    pub shape: Shape,
     // 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,
     pub config: &'a Config,
 }
 
-pub fn format_fn_args<I>(items: I, width: usize, offset: Indent, config: &Config) -> Option<String>
+pub fn format_fn_args<I>(items: I, shape: Shape, config: &Config) -> Option<String>
     where I: Iterator<Item = ListItem>
 {
     list_helper(items,
-                width,
-                offset,
+                shape,
                 config,
                 ListTactic::LimitedHorizontalVertical(config.fn_call_width))
 }
 
-pub fn format_item_list<I>(items: I,
-                           width: usize,
-                           offset: Indent,
-                           config: &Config)
-                           -> Option<String>
+pub fn format_item_list<I>(items: I, shape: Shape, config: &Config) -> Option<String>
     where I: Iterator<Item = ListItem>
 {
-    list_helper(items, width, offset, config, ListTactic::HorizontalVertical)
+    list_helper(items, shape, config, ListTactic::HorizontalVertical)
 }
 
-pub fn list_helper<I>(items: I,
-                      width: usize,
-                      offset: Indent,
-                      config: &Config,
-                      tactic: ListTactic)
-                      -> Option<String>
+pub fn list_helper<I>(items: I, shape: Shape, config: &Config, tactic: ListTactic) -> Option<String>
     where I: Iterator<Item = ListItem>
 {
     let item_vec: Vec<_> = items.collect();
-    let tactic = definitive_tactic(&item_vec, tactic, width);
+    let tactic = definitive_tactic(&item_vec, tactic, shape.width);
     let fmt = ListFormatting {
         tactic: tactic,
         separator: ",",
         trailing_separator: SeparatorTactic::Never,
-        indent: offset,
-        width: width,
+        shape: shape,
         ends_with_newline: false,
         config: config,
     };
@@ -201,7 +189,7 @@ pub fn write_list<I, T>(items: I, formatting: &ListFormatting) -> Option<String>
     let mut iter = items.into_iter().enumerate().peekable();
 
     let mut line_len = 0;
-    let indent_str = &formatting.indent.to_string(formatting.config);
+    let indent_str = &formatting.shape.indent.to_string(formatting.config);
     while let Some((i, item)) = iter.next() {
         let item = item.as_ref();
         let inner_item = try_opt!(item.item.as_ref());
@@ -234,7 +222,7 @@ pub fn write_list<I, T>(items: I, formatting: &ListFormatting) -> Option<String>
                 let total_width = total_item_width(item) + item_sep_len;
 
                 // 1 is space between separator and item.
-                if line_len > 0 && line_len + 1 + total_width > formatting.width {
+                if line_len > 0 && line_len + 1 + total_width > formatting.shape.width {
                     result.push('\n');
                     result.push_str(indent_str);
                     line_len = 0;
@@ -255,12 +243,8 @@ pub fn write_list<I, T>(items: I, formatting: &ListFormatting) -> Option<String>
             // Block style in non-vertical mode.
             let block_mode = tactic != DefinitiveListTactic::Vertical;
             // Width restriction is only relevant in vertical mode.
-            let max_width = formatting.width;
-            let comment = try_opt!(rewrite_comment(comment,
-                                                   block_mode,
-                                                   max_width,
-                                                   formatting.indent,
-                                                   formatting.config));
+            let comment =
+                try_opt!(rewrite_comment(comment, block_mode, formatting.shape, formatting.config));
             result.push_str(&comment);
 
             if tactic == DefinitiveListTactic::Vertical {
@@ -276,11 +260,11 @@ pub fn write_list<I, T>(items: I, formatting: &ListFormatting) -> Option<String>
         // Post-comments
         if tactic != DefinitiveListTactic::Vertical && item.post_comment.is_some() {
             let comment = item.post_comment.as_ref().unwrap();
-            let formatted_comment = try_opt!(rewrite_comment(comment,
-                                                             true,
-                                                             formatting.width,
-                                                             Indent::empty(),
-                                                             formatting.config));
+            let formatted_comment =
+                try_opt!(rewrite_comment(comment,
+                                         true,
+                                         Shape::legacy(formatting.shape.width, Indent::empty()),
+                                         formatting.config));
 
             result.push(' ');
             result.push_str(&formatted_comment);
@@ -292,8 +276,8 @@ pub fn write_list<I, T>(items: I, formatting: &ListFormatting) -> Option<String>
 
         if tactic == DefinitiveListTactic::Vertical && item.post_comment.is_some() {
             // 1 = space between item and comment.
-            let width = formatting.width.checked_sub(item_last_line_width + 1).unwrap_or(1);
-            let mut offset = formatting.indent;
+            let width = formatting.shape.width.checked_sub(item_last_line_width + 1).unwrap_or(1);
+            let mut offset = formatting.shape.indent;
             offset.alignment += item_last_line_width + 1;
             let comment = item.post_comment.as_ref().unwrap();
 
@@ -303,8 +287,10 @@ pub fn write_list<I, T>(items: I, formatting: &ListFormatting) -> Option<String>
                               comment.trim().contains('\n') ||
                               comment.trim().len() > width;
 
-            let formatted_comment =
-                try_opt!(rewrite_comment(comment, block_style, width, offset, formatting.config));
+            let formatted_comment = try_opt!(rewrite_comment(comment,
+                                                             block_style,
+                                                             Shape::legacy(width, offset),
+                                                             formatting.config));
 
             result.push(' ');
             result.push_str(&formatted_comment);
diff --git a/src/macros.rs b/src/macros.rs
index 412fb15cede..3a184d242ef 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -26,7 +26,7 @@ use syntax::parse::tts_to_parser;
 use syntax::symbol;
 use syntax::util::ThinVec;
 
-use Indent;
+use Shape;
 use codemap::SpanUtils;
 use rewrite::{Rewrite, RewriteContext};
 use expr::{rewrite_call, rewrite_array};
@@ -62,13 +62,12 @@ impl MacroStyle {
 pub fn rewrite_macro(mac: &ast::Mac,
                      extra_ident: Option<ast::Ident>,
                      context: &RewriteContext,
-                     width: usize,
-                     offset: Indent,
+                     shape: Shape,
                      position: MacroPosition)
                      -> Option<String> {
     if context.config.use_try_shorthand {
         if let Some(expr) = convert_try_mac(mac, context) {
-            return expr.rewrite(context, width, offset);
+            return expr.rewrite(context, shape);
         }
     }
 
@@ -146,11 +145,12 @@ pub fn rewrite_macro(mac: &ast::Mac,
     match style {
         MacroStyle::Parens => {
             // Format macro invocation as function call.
-            rewrite_call(context, &macro_name, &expr_vec, mac.span, width, offset)
-                .map(|rw| match position {
+            rewrite_call(context, &macro_name, &expr_vec, mac.span, shape).map(|rw| {
+                match position {
                     MacroPosition::Item => format!("{};", rw),
                     _ => rw,
-                })
+                }
+            })
         }
         MacroStyle::Brackets => {
             // Format macro invocation as array literal.
@@ -161,8 +161,9 @@ pub fn rewrite_macro(mac: &ast::Mac,
                                                                        original_style.opener()),
                                                        mac.span.hi - BytePos(1)),
                                                  context,
-                                                 try_opt!(width.checked_sub(extra_offset)),
-                                                 offset + extra_offset));
+                                                 Shape::legacy(try_opt!(shape.width
+                                                                   .checked_sub(extra_offset)),
+                                                               shape.indent + extra_offset)));
 
             Some(format!("{}{}", macro_name, rewrite))
         }
diff --git a/src/missed_spans.rs b/src/missed_spans.rs
index eff3fd6c573..9967a8a4ab8 100644
--- a/src/missed_spans.rs
+++ b/src/missed_spans.rs
@@ -12,6 +12,7 @@ use config::WriteMode;
 use visitor::FmtVisitor;
 use syntax::codemap::{self, BytePos, Span, Pos};
 use comment::{CodeCharKind, CommentCodeSlices, rewrite_comment};
+use Shape;
 
 impl<'a> FmtVisitor<'a> {
     fn output_at_start(&self) -> bool {
@@ -143,8 +144,8 @@ impl<'a> FmtVisitor<'a> {
 
                     self.buffer.push_str(&rewrite_comment(subslice,
                                                           false,
-                                                          comment_width,
-                                                          self.block_indent,
+                                                          Shape::legacy(comment_width,
+                                                                        self.block_indent),
                                                           self.config)
                         .unwrap());
 
diff --git a/src/patterns.rs b/src/patterns.rs
index 03598a1971b..7346dc08010 100644
--- a/src/patterns.rs
+++ b/src/patterns.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use Indent;
+use Shape;
 use codemap::SpanUtils;
 use rewrite::{Rewrite, RewriteContext};
 use utils::{wrap_str, format_mutability};
@@ -23,9 +23,9 @@ use syntax::ptr;
 use syntax::codemap::{self, BytePos, Span};
 
 impl Rewrite for Pat {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
         match self.node {
-            PatKind::Box(ref pat) => rewrite_unary_prefix(context, "box ", &**pat, width, offset),
+            PatKind::Box(ref pat) => rewrite_unary_prefix(context, "box ", &**pat, shape),
             PatKind::Ident(binding_mode, ident, ref sub_pat) => {
                 let (prefix, mutability) = match binding_mode {
                     BindingMode::ByRef(mutability) => ("ref ", mutability),
@@ -36,60 +36,54 @@ impl Rewrite for Pat {
                 let sub_pat = match *sub_pat {
                     Some(ref p) => {
                         // 3 - ` @ `.
-                        let width = try_opt!(width.checked_sub(prefix.len() + mut_infix.len() +
-                                                               id_str.len() +
-                                                               3));
-                        format!(" @ {}", try_opt!(p.rewrite(context, width, offset)))
+                        let width = try_opt!(shape.width
+                            .checked_sub(prefix.len() + mut_infix.len() + id_str.len() + 3));
+                        format!(" @ {}",
+                                try_opt!(p.rewrite(context, Shape::legacy(width, shape.indent))))
                     }
                     None => "".to_owned(),
                 };
 
                 let result = format!("{}{}{}{}", prefix, mut_infix, id_str, sub_pat);
-                wrap_str(result, context.config.max_width, width, offset)
+                wrap_str(result, context.config.max_width, shape)
             }
             PatKind::Wild => {
-                if 1 <= width {
+                if 1 <= shape.width {
                     Some("_".to_owned())
                 } else {
                     None
                 }
             }
             PatKind::Range(ref lhs, ref rhs) => {
-                rewrite_pair(&**lhs, &**rhs, "", "...", "", context, width, offset)
+                rewrite_pair(&**lhs, &**rhs, "", "...", "", context, shape)
             }
             PatKind::Ref(ref pat, mutability) => {
                 let prefix = format!("&{}", format_mutability(mutability));
-                rewrite_unary_prefix(context, &prefix, &**pat, width, offset)
+                rewrite_unary_prefix(context, &prefix, &**pat, shape)
             }
             PatKind::Tuple(ref items, dotdot_pos) => {
-                rewrite_tuple_pat(items, dotdot_pos, None, self.span, context, width, offset)
+                rewrite_tuple_pat(items, dotdot_pos, None, self.span, context, shape)
             }
             PatKind::Path(ref q_self, ref path) => {
-                rewrite_path(context,
-                             PathContext::Expr,
-                             q_self.as_ref(),
-                             path,
-                             width,
-                             offset)
+                rewrite_path(context, PathContext::Expr, q_self.as_ref(), path, shape)
             }
             PatKind::TupleStruct(ref path, ref pat_vec, dotdot_pos) => {
                 let path_str =
-                    try_opt!(rewrite_path(context, PathContext::Expr, None, path, width, offset));
+                    try_opt!(rewrite_path(context, PathContext::Expr, None, path, shape));
                 rewrite_tuple_pat(pat_vec,
                                   dotdot_pos,
                                   Some(path_str),
                                   self.span,
                                   context,
-                                  width,
-                                  offset)
+                                  shape)
             }
-            PatKind::Lit(ref expr) => expr.rewrite(context, width, offset),
+            PatKind::Lit(ref expr) => expr.rewrite(context, shape),
             PatKind::Slice(ref prefix, ref slice_pat, ref suffix) => {
                 // Rewrite all the sub-patterns.
-                let prefix = prefix.iter().map(|p| p.rewrite(context, width, offset));
+                let prefix = prefix.iter().map(|p| p.rewrite(context, shape));
                 let slice_pat = slice_pat.as_ref()
-                    .map(|p| Some(format!("{}..", try_opt!(p.rewrite(context, width, offset)))));
-                let suffix = suffix.iter().map(|p| p.rewrite(context, width, offset));
+                    .map(|p| Some(format!("{}..", try_opt!(p.rewrite(context, shape)))));
+                let suffix = suffix.iter().map(|p| p.rewrite(context, shape));
 
                 // Munge them together.
                 let pats: Option<Vec<String>> = prefix.chain(slice_pat.into_iter())
@@ -105,32 +99,33 @@ impl Rewrite for Pat {
                 } else {
                     format!("[{}]", pats.join(", "))
                 };
-                wrap_str(result, context.config.max_width, width, offset)
+                wrap_str(result, context.config.max_width, shape)
             }
             PatKind::Struct(ref path, ref fields, elipses) => {
-                let path =
-                    try_opt!(rewrite_path(context, PathContext::Expr, None, path, width, offset));
+                let path = try_opt!(rewrite_path(context, PathContext::Expr, None, path, shape));
 
                 let (elipses_str, terminator) = if elipses { (", ..", "..") } else { ("", "}") };
 
                 // 5 = `{` plus space before and after plus `}` plus space before.
-                let budget = try_opt!(width.checked_sub(path.len() + 5 + elipses_str.len()));
+                let budget = try_opt!(shape.width.checked_sub(path.len() + 5 + elipses_str.len()));
                 // FIXME Using visual indenting, should use block or visual to match
                 // struct lit preference (however, in practice I think it is rare
                 // for struct patterns to be multi-line).
                 // 3 = `{` plus space before and after.
-                let offset = offset + path.len() + 3;
+                let offset = shape.indent + path.len() + 3;
 
-                let items = itemize_list(context.codemap,
-                                         fields.iter(),
-                                         terminator,
-                                         |f| f.span.lo,
-                                         |f| f.span.hi,
-                                         |f| f.node.rewrite(context, budget, offset),
-                                         context.codemap.span_after(self.span, "{"),
-                                         self.span.hi);
-                let mut field_string =
-                    try_opt!(format_item_list(items, budget, offset, context.config));
+                let items =
+                    itemize_list(context.codemap,
+                                 fields.iter(),
+                                 terminator,
+                                 |f| f.span.lo,
+                                 |f| f.span.hi,
+                                 |f| f.node.rewrite(context, Shape::legacy(budget, offset)),
+                                 context.codemap.span_after(self.span, "{"),
+                                 self.span.hi);
+                let mut field_string = try_opt!(format_item_list(items,
+                                                                 Shape::legacy(budget, offset),
+                                                                 context.config));
                 if elipses {
                     if field_string.contains('\n') {
                         field_string.push_str(",\n");
@@ -152,25 +147,21 @@ impl Rewrite for Pat {
             }
             // FIXME(#819) format pattern macros.
             PatKind::Mac(..) => {
-                wrap_str(context.snippet(self.span),
-                         context.config.max_width,
-                         width,
-                         offset)
+                wrap_str(context.snippet(self.span), context.config.max_width, shape)
             }
         }
     }
 }
 
 impl Rewrite for FieldPat {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
-        let pat = self.pat.rewrite(context, width, offset);
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
+        let pat = self.pat.rewrite(context, shape);
         if self.is_shorthand {
             pat
         } else {
             wrap_str(format!("{}: {}", self.ident.to_string(), try_opt!(pat)),
                      context.config.max_width,
-                     width,
-                     offset)
+                     shape)
         }
     }
 }
@@ -182,9 +173,9 @@ enum TuplePatField<'a> {
 }
 
 impl<'a> Rewrite for TuplePatField<'a> {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
         match *self {
-            TuplePatField::Pat(ref p) => p.rewrite(context, width, offset),
+            TuplePatField::Pat(ref p) => p.rewrite(context, shape),
             TuplePatField::Dotdot(_) => Some("..".to_string()),
         }
     }
@@ -204,8 +195,7 @@ fn rewrite_tuple_pat(pats: &[ptr::P<ast::Pat>],
                      path_str: Option<String>,
                      span: Span,
                      context: &RewriteContext,
-                     width: usize,
-                     offset: Indent)
+                     shape: Shape)
                      -> Option<String> {
     let mut pat_vec: Vec<_> = pats.into_iter().map(|x| TuplePatField::Pat(x)).collect();
 
@@ -230,18 +220,19 @@ fn rewrite_tuple_pat(pats: &[ptr::P<ast::Pat>],
 
         let path_len = path_str.as_ref().map(|p| p.len()).unwrap_or(0);
         // 2 = "()".len(), 3 = "(,)".len()
-        let width = try_opt!(width.checked_sub(path_len + if add_comma { 3 } else { 2 }));
+        let width = try_opt!(shape.width.checked_sub(path_len + if add_comma { 3 } else { 2 }));
         // 1 = "(".len()
-        let offset = offset + path_len + 1;
-        let mut items: Vec<_> = itemize_list(context.codemap,
-                                             pat_vec.iter(),
-                                             if add_comma { ",)" } else { ")" },
-                                             |item| item.span().lo,
-                                             |item| item.span().hi,
-                                             |item| item.rewrite(context, width, offset),
-                                             context.codemap.span_after(span, "("),
-                                             span.hi - BytePos(1))
-            .collect();
+        let offset = shape.indent + path_len + 1;
+        let mut items: Vec<_> =
+            itemize_list(context.codemap,
+                         pat_vec.iter(),
+                         if add_comma { ",)" } else { ")" },
+                         |item| item.span().lo,
+                         |item| item.span().hi,
+                         |item| item.rewrite(context, Shape::legacy(width, offset)),
+                         context.codemap.span_after(span, "("),
+                         span.hi - BytePos(1))
+                .collect();
 
         // Condense wildcard string suffix into a single ..
         let wildcard_suffix_len = count_wildcard_suffix_len(&items);
@@ -251,9 +242,11 @@ fn rewrite_tuple_pat(pats: &[ptr::P<ast::Pat>],
             items[new_item_count - 1].item = Some("..".to_owned());
 
             let da_iter = items.into_iter().take(new_item_count);
-            try_opt!(format_item_list(da_iter, width, offset, context.config))
+            try_opt!(format_item_list(da_iter, Shape::legacy(width, offset), context.config))
         } else {
-            try_opt!(format_item_list(items.into_iter(), width, offset, context.config))
+            try_opt!(format_item_list(items.into_iter(),
+                                      Shape::legacy(width, offset),
+                                      context.config))
         };
 
         match path_str {
diff --git a/src/rewrite.rs b/src/rewrite.rs
index 247b1df1c3f..3c69e2c3c45 100644
--- a/src/rewrite.rs
+++ b/src/rewrite.rs
@@ -13,18 +13,12 @@
 use syntax::codemap::{CodeMap, Span};
 use syntax::parse::ParseSess;
 
-use Indent;
+use {Indent, Shape};
 use config::Config;
 
 pub trait Rewrite {
-    /// Rewrite self into offset and width.
-    /// `offset` is the indentation of the first line. The next lines
-    /// should begin with a least `offset` spaces (except backwards
-    /// indentation). The first line should not begin with indentation.
-    /// `width` is the maximum number of characters on the last line
-    /// (excluding offset). The width of other lines is not limited by
-    /// `width`.
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String>;
+    /// Rewrite self into shape.
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String>;
 }
 
 #[derive(Clone)]
diff --git a/src/string.rs b/src/string.rs
index 71d8f8415f0..014830a9155 100644
--- a/src/string.rs
+++ b/src/string.rs
@@ -13,7 +13,7 @@
 use unicode_segmentation::UnicodeSegmentation;
 use regex::Regex;
 
-use Indent;
+use Shape;
 use config::Config;
 use utils::wrap_str;
 
@@ -24,8 +24,7 @@ pub struct StringFormat<'a> {
     pub closer: &'a str,
     pub line_start: &'a str,
     pub line_end: &'a str,
-    pub width: usize,
-    pub offset: Indent,
+    pub shape: Shape,
     pub trim_end: bool,
     pub config: &'a Config,
 }
@@ -37,7 +36,7 @@ pub fn rewrite_string<'a>(orig: &str, fmt: &StringFormat<'a>) -> Option<String>
     let stripped_str = re.replace_all(orig, "$1");
 
     let graphemes = UnicodeSegmentation::graphemes(&*stripped_str, false).collect::<Vec<&str>>();
-    let indent = fmt.offset.to_string(fmt.config);
+    let indent = fmt.shape.indent.to_string(fmt.config);
     let punctuation = ":,;.";
 
     // `cur_start` is the position in `orig` of the start of the current line.
@@ -50,7 +49,7 @@ pub fn rewrite_string<'a>(orig: &str, fmt: &StringFormat<'a>) -> Option<String>
     let ender_length = fmt.line_end.len();
     // If we cannot put at least a single character per line, the rewrite won't
     // succeed.
-    let max_chars = try_opt!(fmt.width.checked_sub(fmt.opener.len() + ender_length + 1)) + 1;
+    let max_chars = try_opt!(fmt.shape.width.checked_sub(fmt.opener.len() + ender_length + 1)) + 1;
 
     // Snip a line at a time from `orig` until it is used up. Push the snippet
     // onto result.
@@ -118,7 +117,7 @@ pub fn rewrite_string<'a>(orig: &str, fmt: &StringFormat<'a>) -> Option<String>
     }
 
     result.push_str(fmt.closer);
-    wrap_str(result, fmt.config.max_width, fmt.width, fmt.offset)
+    wrap_str(result, fmt.config.max_width, fmt.shape)
 }
 
 #[cfg(test)]
@@ -133,8 +132,7 @@ mod test {
             closer: "\"",
             line_start: " ",
             line_end: "\\",
-            width: 2,
-            offset: ::Indent::empty(),
+            shape: ::Shape::legacy(2, ::Indent::empty()),
             trim_end: false,
             config: &config,
         };
diff --git a/src/types.rs b/src/types.rs
index acc9c7c6cb5..cce487cb2ea 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -17,7 +17,7 @@ use syntax::codemap::{self, Span, BytePos};
 use syntax::print::pprust;
 use syntax::symbol::keywords;
 
-use {Indent, Spanned};
+use {Shape, Spanned};
 use codemap::SpanUtils;
 use lists::{format_item_list, itemize_list, format_fn_args};
 use rewrite::{Rewrite, RewriteContext};
@@ -38,8 +38,7 @@ pub fn rewrite_path(context: &RewriteContext,
                     path_context: PathContext,
                     qself: Option<&ast::QSelf>,
                     path: &ast::Path,
-                    width: usize,
-                    offset: Indent)
+                    shape: Shape)
                     -> Option<String> {
     let skip_count = qself.map_or(0, |x| x.position);
 
@@ -58,7 +57,7 @@ pub fn rewrite_path(context: &RewriteContext,
             result.push_str(" ")
         }
 
-        let fmt_ty = try_opt!(qself.ty.rewrite(context, width, offset));
+        let fmt_ty = try_opt!(qself.ty.rewrite(context, shape));
         result.push_str(&fmt_ty);
 
         if skip_count > 0 {
@@ -67,9 +66,9 @@ pub fn rewrite_path(context: &RewriteContext,
                 result.push_str("::");
             }
 
-            let extra_offset = extra_offset(&result, offset);
+            let extra_offset = extra_offset(&result, shape.indent);
             // 3 = ">::".len()
-            let budget = try_opt!(width.checked_sub(extra_offset + 3));
+            let budget = try_opt!(shape.width.checked_sub(extra_offset + 3));
 
             result = try_opt!(rewrite_path_segments(PathContext::Type,
                                                     result,
@@ -77,8 +76,8 @@ pub fn rewrite_path(context: &RewriteContext,
                                                     span_lo,
                                                     path.span.hi,
                                                     context,
-                                                    budget,
-                                                    offset + extra_offset));
+                                                    Shape::legacy(budget,
+                                                                  shape.indent + extra_offset)));
         }
 
         if context.config.spaces_within_angle_brackets {
@@ -89,16 +88,15 @@ pub fn rewrite_path(context: &RewriteContext,
         span_lo = qself.ty.span.hi + BytePos(1);
     }
 
-    let extra_offset = extra_offset(&result, offset);
-    let budget = try_opt!(width.checked_sub(extra_offset));
+    let extra_offset = extra_offset(&result, shape.indent);
+    let budget = try_opt!(shape.width.checked_sub(extra_offset));
     rewrite_path_segments(path_context,
                           result,
                           path.segments.iter().skip(skip_count),
                           span_lo,
                           path.span.hi,
                           context,
-                          budget,
-                          offset + extra_offset)
+                          Shape::legacy(budget, shape.indent + extra_offset))
 }
 
 fn rewrite_path_segments<'a, I>(path_context: PathContext,
@@ -107,8 +105,7 @@ fn rewrite_path_segments<'a, I>(path_context: PathContext,
                                 mut span_lo: BytePos,
                                 span_hi: BytePos,
                                 context: &RewriteContext,
-                                width: usize,
-                                offset: Indent)
+                                shape: Shape)
                                 -> Option<String>
     where I: Iterator<Item = &'a ast::PathSegment>
 {
@@ -125,16 +122,15 @@ fn rewrite_path_segments<'a, I>(path_context: PathContext,
             buffer.push_str("::");
         }
 
-        let extra_offset = extra_offset(&buffer, offset);
-        let remaining_width = try_opt!(width.checked_sub(extra_offset));
-        let new_offset = offset + extra_offset;
+        let extra_offset = extra_offset(&buffer, shape.indent);
+        let remaining_width = try_opt!(shape.width.checked_sub(extra_offset));
+        let new_offset = shape.indent + extra_offset;
         let segment_string = try_opt!(rewrite_segment(path_context,
                                                       segment,
                                                       &mut span_lo,
                                                       span_hi,
                                                       context,
-                                                      remaining_width,
-                                                      new_offset));
+                                                      Shape::legacy(remaining_width, new_offset)));
 
         buffer.push_str(&segment_string);
     }
@@ -160,14 +156,15 @@ impl<'a> SegmentParam<'a> {
 }
 
 impl<'a> Rewrite for SegmentParam<'a> {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
         match *self {
-            SegmentParam::LifeTime(lt) => lt.rewrite(context, width, offset),
-            SegmentParam::Type(ty) => ty.rewrite(context, width, offset),
+            SegmentParam::LifeTime(lt) => lt.rewrite(context, shape),
+            SegmentParam::Type(ty) => ty.rewrite(context, shape),
             SegmentParam::Binding(binding) => {
                 let mut result = format!("{} = ", binding.ident);
-                let budget = try_opt!(width.checked_sub(result.len()));
-                let rewrite = try_opt!(binding.ty.rewrite(context, budget, offset + result.len()));
+                let budget = try_opt!(shape.width.checked_sub(result.len()));
+                let rewrite = try_opt!(binding.ty
+                    .rewrite(context, Shape::legacy(budget, shape.indent + result.len())));
                 result.push_str(&rewrite);
                 Some(result)
             }
@@ -190,12 +187,11 @@ fn rewrite_segment(path_context: PathContext,
                    span_lo: &mut BytePos,
                    span_hi: BytePos,
                    context: &RewriteContext,
-                   width: usize,
-                   offset: Indent)
+                   shape: Shape)
                    -> Option<String> {
     let ident_len = segment.identifier.to_string().len();
-    let width = try_opt!(width.checked_sub(ident_len));
-    let offset = offset + ident_len;
+    let width = try_opt!(shape.width.checked_sub(ident_len));
+    let offset = shape.indent + ident_len;
 
     let params = if let Some(ref params) = segment.parameters {
         match **params {
@@ -222,18 +218,21 @@ fn rewrite_segment(path_context: PathContext,
                 // 1 for >
                 let list_width = try_opt!(width.checked_sub(extra_offset + 1));
 
-                let items =
-                    itemize_list(context.codemap,
-                                 param_list.into_iter(),
-                                 ">",
-                                 |param| param.get_span().lo,
-                                 |param| param.get_span().hi,
-                                 |seg| seg.rewrite(context, list_width, offset + extra_offset),
-                                 list_lo,
-                                 span_hi);
+                let items = itemize_list(context.codemap,
+                                         param_list.into_iter(),
+                                         ">",
+                                         |param| param.get_span().lo,
+                                         |param| param.get_span().hi,
+                                         |seg| {
+                                             seg.rewrite(context,
+                                                         Shape::legacy(list_width,
+                                                                       offset + extra_offset))
+                                         },
+                                         list_lo,
+                                         span_hi);
                 let list_str = try_opt!(format_item_list(items,
-                                                         list_width,
-                                                         offset + extra_offset,
+                                                         Shape::legacy(list_width,
+                                                                       offset + extra_offset),
                                                          context.config));
 
                 // Update position of last bracket.
@@ -255,8 +254,7 @@ fn rewrite_segment(path_context: PathContext,
                                               false,
                                               data.span,
                                               context,
-                                              width,
-                                              offset))
+                                              Shape::legacy(width, offset)))
             }
             _ => String::new(),
         }
@@ -272,8 +270,7 @@ fn format_function_type<'a, I>(inputs: I,
                                variadic: bool,
                                span: Span,
                                context: &RewriteContext,
-                               width: usize,
-                               offset: Indent)
+                               shape: Shape)
                                -> Option<String>
     where I: ExactSizeIterator,
           <I as Iterator>::Item: Deref,
@@ -297,44 +294,45 @@ fn format_function_type<'a, I>(inputs: I,
     };
 
     // 2 for ()
-    let budget = try_opt!(width.checked_sub(2));
+    let budget = try_opt!(shape.width.checked_sub(2));
     // 1 for (
-    let offset = offset + 1;
+    let offset = shape.indent + 1;
     let list_lo = context.codemap.span_after(span, "(");
-    let items =
-        itemize_list(context.codemap,
-                     // FIXME Would be nice to avoid this allocation,
-                     // but I couldn't get the types to work out.
-                     inputs.map(|i| ArgumentKind::Regular(Box::new(i)))
-                         .chain(variadic_arg),
-                     ")",
-                     |arg| match *arg {
-                         ArgumentKind::Regular(ref ty) => ty.span().lo,
-                         ArgumentKind::Variadic(start) => start,
-                     },
-                     |arg| match *arg {
-                         ArgumentKind::Regular(ref ty) => ty.span().hi,
-                         ArgumentKind::Variadic(start) => start + BytePos(3),
-                     },
-                     |arg| match *arg {
-                         ArgumentKind::Regular(ref ty) => ty.rewrite(context, budget, offset),
-                         ArgumentKind::Variadic(_) => Some("...".to_owned()),
-                     },
-                     list_lo,
-                     span.hi);
-
-    let list_str = try_opt!(format_fn_args(items, budget, offset, context.config));
+    let items = itemize_list(context.codemap,
+                             // FIXME Would be nice to avoid this allocation,
+                             // but I couldn't get the types to work out.
+                             inputs.map(|i| ArgumentKind::Regular(Box::new(i)))
+                                 .chain(variadic_arg),
+                             ")",
+                             |arg| match *arg {
+                                 ArgumentKind::Regular(ref ty) => ty.span().lo,
+                                 ArgumentKind::Variadic(start) => start,
+                             },
+                             |arg| match *arg {
+                                 ArgumentKind::Regular(ref ty) => ty.span().hi,
+                                 ArgumentKind::Variadic(start) => start + BytePos(3),
+                             },
+                             |arg| match *arg {
+                                 ArgumentKind::Regular(ref ty) => {
+                                     ty.rewrite(context, Shape::legacy(budget, offset))
+                                 }
+                                 ArgumentKind::Variadic(_) => Some("...".to_owned()),
+                             },
+                             list_lo,
+                             span.hi);
+
+    let list_str = try_opt!(format_fn_args(items, Shape::legacy(budget, offset), context.config));
 
     let output = match *output {
         FunctionRetTy::Ty(ref ty) => {
-            let budget = try_opt!(width.checked_sub(4));
-            let type_str = try_opt!(ty.rewrite(context, budget, offset + 4));
+            let budget = try_opt!(shape.width.checked_sub(4));
+            let type_str = try_opt!(ty.rewrite(context, Shape::legacy(budget, offset + 4)));
             format!(" -> {}", type_str)
         }
         FunctionRetTy::Default(..) => String::new(),
     };
 
-    let infix = if !output.is_empty() && output.len() + list_str.len() > width {
+    let infix = if !output.is_empty() && output.len() + list_str.len() > shape.width {
         format!("\n{}", (offset - 1).to_string(context.config))
     } else {
         String::new()
@@ -357,35 +355,33 @@ fn type_bound_colon(context: &RewriteContext) -> &'static str {
 }
 
 impl Rewrite for ast::WherePredicate {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
         // TODO: dead spans?
         let result = match *self {
             ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { ref bound_lifetimes,
                                                                            ref bounded_ty,
                                                                            ref bounds,
                                                                            .. }) => {
-                let type_str = try_opt!(bounded_ty.rewrite(context, width, offset));
+                let type_str = try_opt!(bounded_ty.rewrite(context, shape));
 
                 let colon = type_bound_colon(context);
 
                 if !bound_lifetimes.is_empty() {
                     let lifetime_str: String = try_opt!(bound_lifetimes.iter()
                                                                .map(|lt| {
-                                                                   lt.rewrite(context,
-                                                                              width,
-                                                                              offset)
+                                                                   lt.rewrite(context, shape)
                                                                })
                                                                .intersperse(Some(", ".to_string()))
                                                                .collect());
 
                     // 6 = "for<> ".len()
                     let used_width = lifetime_str.len() + type_str.len() + colon.len() + 6;
-                    let budget = try_opt!(width.checked_sub(used_width));
+                    let budget = try_opt!(shape.width.checked_sub(used_width));
                     let bounds_str: String = try_opt!(bounds.iter()
                                                     .map(|ty_bound| {
                                                         ty_bound.rewrite(context,
-                                                                         budget,
-                                                                         offset + used_width)
+                                                                         Shape::legacy(budget,
+                                                                         shape.indent + used_width))
                                                     })
                                                     .intersperse(Some(" + ".to_string()))
                                                     .collect());
@@ -397,12 +393,12 @@ impl Rewrite for ast::WherePredicate {
                     }
                 } else {
                     let used_width = type_str.len() + colon.len();
-                    let budget = try_opt!(width.checked_sub(used_width));
+                    let budget = try_opt!(shape.width.checked_sub(used_width));
                     let bounds_str: String = try_opt!(bounds.iter()
                                                     .map(|ty_bound| {
                                                         ty_bound.rewrite(context,
-                                                                         budget,
-                                                                         offset + used_width)
+                                                                         Shape::legacy(budget,
+                                                                         shape.indent + used_width))
                                                     })
                                                     .intersperse(Some(" + ".to_string()))
                                                     .collect());
@@ -413,91 +409,92 @@ impl Rewrite for ast::WherePredicate {
             ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { ref lifetime,
                                                                              ref bounds,
                                                                              .. }) => {
-                try_opt!(rewrite_bounded_lifetime(lifetime, bounds.iter(), context, width, offset))
+                try_opt!(rewrite_bounded_lifetime(lifetime, bounds.iter(), context, shape))
             }
             ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { ref lhs_ty,
                                                                      ref rhs_ty,
                                                                      .. }) => {
-                let lhs_ty_str = try_opt!(lhs_ty.rewrite(context, width, offset));
+                let lhs_ty_str = try_opt!(lhs_ty.rewrite(context, shape));
                 // 3 = " = ".len()
                 let used_width = 3 + lhs_ty_str.len();
-                let budget = try_opt!(width.checked_sub(used_width));
-                let rhs_ty_str = try_opt!(rhs_ty.rewrite(context, budget, offset + used_width));
+                let budget = try_opt!(shape.width.checked_sub(used_width));
+                let rhs_ty_str = try_opt!(rhs_ty.rewrite(context,
+                                            Shape::legacy(budget, shape.indent + used_width)));
                 format!("{} = {}", lhs_ty_str, rhs_ty_str)
             }
         };
 
-        wrap_str(result, context.config.max_width, width, offset)
+        wrap_str(result, context.config.max_width, shape)
     }
 }
 
 impl Rewrite for ast::LifetimeDef {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
-        rewrite_bounded_lifetime(&self.lifetime, self.bounds.iter(), context, width, offset)
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
+        rewrite_bounded_lifetime(&self.lifetime, self.bounds.iter(), context, shape)
     }
 }
 
 fn rewrite_bounded_lifetime<'b, I>(lt: &ast::Lifetime,
                                    bounds: I,
                                    context: &RewriteContext,
-                                   width: usize,
-                                   offset: Indent)
+                                   shape: Shape)
                                    -> Option<String>
     where I: ExactSizeIterator<Item = &'b ast::Lifetime>
 {
-    let result = try_opt!(lt.rewrite(context, width, offset));
+    let result = try_opt!(lt.rewrite(context, shape));
 
     if bounds.len() == 0 {
         Some(result)
     } else {
         let appendix: Vec<_> = try_opt!(bounds.into_iter()
-            .map(|b| b.rewrite(context, width, offset))
+            .map(|b| b.rewrite(context, shape))
             .collect());
         let colon = type_bound_colon(context);
         let result = format!("{}{}{}", result, colon, appendix.join(" + "));
-        wrap_str(result, context.config.max_width, width, offset)
+        wrap_str(result, context.config.max_width, shape)
     }
 }
 
 impl Rewrite for ast::TyParamBound {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
         match *self {
             ast::TyParamBound::TraitTyParamBound(ref tref, ast::TraitBoundModifier::None) => {
-                tref.rewrite(context, width, offset)
+                tref.rewrite(context, shape)
             }
             ast::TyParamBound::TraitTyParamBound(ref tref, ast::TraitBoundModifier::Maybe) => {
-                let budget = try_opt!(width.checked_sub(1));
-                Some(format!("?{}", try_opt!(tref.rewrite(context, budget, offset + 1))))
+                let budget = try_opt!(shape.width.checked_sub(1));
+                Some(format!("?{}",
+                             try_opt!(tref.rewrite(context,
+                                                   Shape::legacy(budget, shape.indent + 1)))))
             }
-            ast::TyParamBound::RegionTyParamBound(ref l) => l.rewrite(context, width, offset),
+            ast::TyParamBound::RegionTyParamBound(ref l) => l.rewrite(context, shape),
         }
     }
 }
 
 impl Rewrite for ast::Lifetime {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
         wrap_str(pprust::lifetime_to_string(self),
                  context.config.max_width,
-                 width,
-                 offset)
+                 shape)
     }
 }
 
 impl Rewrite for ast::TyParamBounds {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
         let joiner = match context.config.type_punctuation_density {
             TypeDensity::Compressed => "+",
             TypeDensity::Wide => " + ",
         };
         let strs: Vec<_> = try_opt!(self.iter()
-            .map(|b| b.rewrite(context, width, offset))
+            .map(|b| b.rewrite(context, shape))
             .collect());
-        wrap_str(strs.join(joiner), context.config.max_width, width, offset)
+        wrap_str(strs.join(joiner), context.config.max_width, shape)
     }
 }
 
 impl Rewrite for ast::TyParam {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
         let mut result = String::with_capacity(128);
         result.push_str(&self.ident.to_string());
         if !self.bounds.is_empty() {
@@ -511,7 +508,7 @@ impl Rewrite for ast::TyParam {
 
             let bounds: String = try_opt!(self.bounds
                 .iter()
-                .map(|ty_bound| ty_bound.rewrite(context, width, offset))
+                .map(|ty_bound| ty_bound.rewrite(context, shape))
                 .intersperse(Some(" + ".to_string()))
                 .collect());
 
@@ -524,29 +521,31 @@ impl Rewrite for ast::TyParam {
                 TypeDensity::Wide => " = ",
             };
             result.push_str(eq_str);
-            let budget = try_opt!(width.checked_sub(result.len()));
-            let rewrite = try_opt!(def.rewrite(context, budget, offset + result.len()));
+            let budget = try_opt!(shape.width.checked_sub(result.len()));
+            let rewrite =
+                try_opt!(def.rewrite(context, Shape::legacy(budget, shape.indent + result.len())));
             result.push_str(&rewrite);
         }
 
-        wrap_str(result, context.config.max_width, width, offset)
+        wrap_str(result, context.config.max_width, shape)
     }
 }
 
 impl Rewrite for ast::PolyTraitRef {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
         if !self.bound_lifetimes.is_empty() {
             let lifetime_str: String = try_opt!(self.bound_lifetimes
                 .iter()
-                .map(|lt| lt.rewrite(context, width, offset))
+                .map(|lt| lt.rewrite(context, shape))
                 .intersperse(Some(", ".to_string()))
                 .collect());
 
             // 6 is "for<> ".len()
             let extra_offset = lifetime_str.len() + 6;
-            let max_path_width = try_opt!(width.checked_sub(extra_offset));
+            let max_path_width = try_opt!(shape.width.checked_sub(extra_offset));
             let path_str = try_opt!(self.trait_ref
-                .rewrite(context, max_path_width, offset + extra_offset));
+                .rewrite(context,
+                         Shape::legacy(max_path_width, shape.indent + extra_offset)));
 
             Some(if context.config.spaces_within_angle_brackets && lifetime_str.len() > 0 {
                 format!("for< {} > {}", lifetime_str, path_str)
@@ -554,58 +553,64 @@ impl Rewrite for ast::PolyTraitRef {
                 format!("for<{}> {}", lifetime_str, path_str)
             })
         } else {
-            self.trait_ref.rewrite(context, width, offset)
+            self.trait_ref.rewrite(context, shape)
         }
     }
 }
 
 impl Rewrite for ast::TraitRef {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
-        rewrite_path(context, PathContext::Type, None, &self.path, width, offset)
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
+        rewrite_path(context, PathContext::Type, None, &self.path, shape)
     }
 }
 
 impl Rewrite for ast::Ty {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
         match self.node {
-            ast::TyKind::TraitObject(ref bounds) => bounds.rewrite(context, width, offset),
+            ast::TyKind::TraitObject(ref bounds) => bounds.rewrite(context, shape),
             ast::TyKind::Ptr(ref mt) => {
                 let prefix = match mt.mutbl {
                     Mutability::Mutable => "*mut ",
                     Mutability::Immutable => "*const ",
                 };
 
-                rewrite_unary_prefix(context, prefix, &*mt.ty, width, offset)
+                rewrite_unary_prefix(context, prefix, &*mt.ty, shape)
             }
             ast::TyKind::Rptr(ref lifetime, ref mt) => {
                 let mut_str = format_mutability(mt.mutbl);
                 let mut_len = mut_str.len();
                 Some(match *lifetime {
                     Some(ref lifetime) => {
-                        let lt_budget = try_opt!(width.checked_sub(2 + mut_len));
-                        let lt_str =
-                            try_opt!(lifetime.rewrite(context, lt_budget, offset + 2 + mut_len));
+                        let lt_budget = try_opt!(shape.width.checked_sub(2 + mut_len));
+                        let lt_str = try_opt!(lifetime.rewrite(context,
+                                                               Shape::legacy(lt_budget,
+                                                                             shape.indent + 2 +
+                                                                             mut_len)));
                         let lt_len = lt_str.len();
-                        let budget = try_opt!(width.checked_sub(2 + mut_len + lt_len));
+                        let budget = try_opt!(shape.width.checked_sub(2 + mut_len + lt_len));
                         format!("&{} {}{}",
                                 lt_str,
                                 mut_str,
                                 try_opt!(mt.ty
-                                    .rewrite(context, budget, offset + 2 + mut_len + lt_len)))
+                                    .rewrite(context,
+                                             Shape::legacy(budget,
+                                                           shape.indent + 2 + mut_len + lt_len))))
                     }
                     None => {
-                        let budget = try_opt!(width.checked_sub(1 + mut_len));
+                        let budget = try_opt!(shape.width.checked_sub(1 + mut_len));
                         format!("&{}{}",
                                 mut_str,
-                                try_opt!(mt.ty.rewrite(context, budget, offset + 1 + mut_len)))
+                                try_opt!(mt.ty.rewrite(context,
+                                                       Shape::legacy(budget,
+                                                                     shape.indent + 1 + mut_len))))
                     }
                 })
             }
             // FIXME: we drop any comments here, even though it's a silly place to put
             // comments.
             ast::TyKind::Paren(ref ty) => {
-                let budget = try_opt!(width.checked_sub(2));
-                ty.rewrite(context, budget, offset + 1)
+                let budget = try_opt!(shape.width.checked_sub(2));
+                ty.rewrite(context, Shape::legacy(budget, shape.indent + 1))
                     .map(|ty_str| if context.config.spaces_within_parens {
                         format!("( {} )", ty_str)
                     } else {
@@ -614,11 +619,11 @@ impl Rewrite for ast::Ty {
             }
             ast::TyKind::Slice(ref ty) => {
                 let budget = if context.config.spaces_within_square_brackets {
-                    try_opt!(width.checked_sub(4))
+                    try_opt!(shape.width.checked_sub(4))
                 } else {
-                    try_opt!(width.checked_sub(2))
+                    try_opt!(shape.width.checked_sub(2))
                 };
-                ty.rewrite(context, budget, offset + 1)
+                ty.rewrite(context, Shape::legacy(budget, shape.indent + 1))
                     .map(|ty_str| if context.config.spaces_within_square_brackets {
                         format!("[ {} ]", ty_str)
                     } else {
@@ -626,41 +631,30 @@ impl Rewrite for ast::Ty {
                     })
             }
             ast::TyKind::Tup(ref items) => {
-                rewrite_tuple(context,
-                              items.iter().map(|x| &**x),
-                              self.span,
-                              width,
-                              offset)
+                rewrite_tuple(context, items.iter().map(|x| &**x), self.span, shape)
             }
             ast::TyKind::Path(ref q_self, ref path) => {
-                rewrite_path(context,
-                             PathContext::Type,
-                             q_self.as_ref(),
-                             path,
-                             width,
-                             offset)
+                rewrite_path(context, PathContext::Type, q_self.as_ref(), path, shape)
             }
             ast::TyKind::Array(ref ty, ref repeats) => {
                 let use_spaces = context.config.spaces_within_square_brackets;
                 let lbr = if use_spaces { "[ " } else { "[" };
                 let rbr = if use_spaces { " ]" } else { "]" };
-                rewrite_pair(&**ty, &**repeats, lbr, "; ", rbr, context, width, offset)
+                rewrite_pair(&**ty, &**repeats, lbr, "; ", rbr, context, shape)
             }
             ast::TyKind::Infer => {
-                if width >= 1 {
+                if shape.width >= 1 {
                     Some("_".to_owned())
                 } else {
                     None
                 }
             }
-            ast::TyKind::BareFn(ref bare_fn) => {
-                rewrite_bare_fn(bare_fn, self.span, context, width, offset)
-            }
+            ast::TyKind::BareFn(ref bare_fn) => rewrite_bare_fn(bare_fn, self.span, context, shape),
             ast::TyKind::Never => Some(String::from("!")),
             ast::TyKind::Mac(..) => None,
             ast::TyKind::ImplicitSelf => Some(String::from("")),
             ast::TyKind::ImplTrait(ref it) => {
-                it.rewrite(context, width, offset).map(|it_str| format!("impl {}", it_str))
+                it.rewrite(context, shape).map(|it_str| format!("impl {}", it_str))
             }
             ast::TyKind::Typeof(..) => unreachable!(),
         }
@@ -670,8 +664,7 @@ impl Rewrite for ast::Ty {
 fn rewrite_bare_fn(bare_fn: &ast::BareFnTy,
                    span: Span,
                    context: &RewriteContext,
-                   width: usize,
-                   offset: Indent)
+                   shape: Shape)
                    -> Option<String> {
     let mut result = String::with_capacity(128);
 
@@ -682,7 +675,10 @@ fn rewrite_bare_fn(bare_fn: &ast::BareFnTy,
         // rightward drift. If that is a problem, we could use the list stuff.
         result.push_str(&try_opt!(bare_fn.lifetimes
             .iter()
-            .map(|l| l.rewrite(context, try_opt!(width.checked_sub(6)), offset + 4))
+            .map(|l| {
+                l.rewrite(context,
+                          Shape::legacy(try_opt!(shape.width.checked_sub(6)), shape.indent + 4))
+            })
             .intersperse(Some(", ".to_string()))
             .collect::<Option<String>>()));
         result.push_str("> ");
@@ -696,16 +692,15 @@ fn rewrite_bare_fn(bare_fn: &ast::BareFnTy,
 
     result.push_str("fn");
 
-    let budget = try_opt!(width.checked_sub(result.len()));
-    let indent = offset + result.len();
+    let budget = try_opt!(shape.width.checked_sub(result.len()));
+    let indent = shape.indent + result.len();
 
     let rewrite = try_opt!(format_function_type(bare_fn.decl.inputs.iter(),
                                                 &bare_fn.decl.output,
                                                 bare_fn.decl.variadic,
                                                 span,
                                                 context,
-                                                budget,
-                                                indent));
+                                                Shape::legacy(budget, indent)));
 
     result.push_str(&rewrite);
 
diff --git a/src/utils.rs b/src/utils.rs
index 2f8c5db82e1..c9017571025 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -18,7 +18,7 @@ use syntax::ast::{self, Visibility, Attribute, MetaItem, MetaItemKind, NestedMet
 use syntax::codemap::BytePos;
 use syntax::abi;
 
-use Indent;
+use {Indent, Shape};
 use rewrite::{Rewrite, RewriteContext};
 
 use SKIP_ANNOTATION;
@@ -253,18 +253,18 @@ macro_rules! source {
 
 // Wraps string-like values in an Option. Returns Some when the string adheres
 // to the Rewrite constraints defined for the Rewrite trait and else otherwise.
-pub fn wrap_str<S: AsRef<str>>(s: S, max_width: usize, width: usize, offset: Indent) -> Option<S> {
+pub fn wrap_str<S: AsRef<str>>(s: S, max_width: usize, shape: Shape) -> Option<S> {
     {
         let snippet = s.as_ref();
 
-        if !snippet.contains('\n') && snippet.len() > width {
+        if !snippet.contains('\n') && snippet.len() > shape.width {
             return None;
         } else {
             let mut lines = snippet.lines();
 
-            // The caller of this function has already placed `offset`
+            // The caller of this function has already placed `shape.offset`
             // characters on the first line.
-            let first_line_max_len = try_opt!(max_width.checked_sub(offset.width()));
+            let first_line_max_len = try_opt!(max_width.checked_sub(shape.indent.width()));
             if lines.next().unwrap().len() > first_line_max_len {
                 return None;
             }
@@ -278,7 +278,7 @@ pub fn wrap_str<S: AsRef<str>>(s: S, max_width: usize, width: usize, offset: Ind
             // indentation.
             // A special check for the last line, since the caller may
             // place trailing characters on this line.
-            if snippet.lines().rev().next().unwrap().len() > offset.width() + width {
+            if snippet.lines().rev().next().unwrap().len() > shape.indent.width() + shape.width {
                 return None;
             }
         }
@@ -288,8 +288,8 @@ pub fn wrap_str<S: AsRef<str>>(s: S, max_width: usize, width: usize, offset: Ind
 }
 
 impl Rewrite for String {
-    fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
-        wrap_str(self, context.config.max_width, width, offset).map(ToOwned::to_owned)
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
+        wrap_str(self, context.config.max_width, shape).map(ToOwned::to_owned)
     }
 }
 
diff --git a/src/visitor.rs b/src/visitor.rs
index 95ec54e89ee..fd0a1f1ed21 100644
--- a/src/visitor.rs
+++ b/src/visitor.rs
@@ -14,7 +14,7 @@ use syntax::parse::ParseSess;
 
 use strings::string_buffer::StringBuffer;
 
-use Indent;
+use {Indent, Shape};
 use utils;
 use codemap::{LineRangeUtils, SpanUtils};
 use config::Config;
@@ -59,8 +59,9 @@ impl<'a> FmtVisitor<'a> {
             ast::StmtKind::Expr(..) |
             ast::StmtKind::Semi(..) => {
                 let rewrite = stmt.rewrite(&self.get_context(),
-                                           self.config.max_width - self.block_indent.width(),
-                                           self.block_indent);
+                                           Shape::legacy(self.config.max_width -
+                                                         self.block_indent.width(),
+                                                         self.block_indent));
                 self.push_rewrite(stmt.span, rewrite);
             }
             ast::StmtKind::Mac(ref mac) => {
@@ -425,8 +426,7 @@ impl<'a> FmtVisitor<'a> {
         let rewrite = rewrite_macro(mac,
                                     ident,
                                     &self.get_context(),
-                                    width,
-                                    self.block_indent,
+                                    Shape::legacy(width, self.block_indent),
                                     pos);
         self.push_rewrite(mac.span, rewrite);
     }
@@ -482,8 +482,8 @@ impl<'a> FmtVisitor<'a> {
         self.format_missing_with_indent(source!(self, first.span).lo);
 
         let rewrite = outers.rewrite(&self.get_context(),
-                     self.config.max_width - self.block_indent.width(),
-                     self.block_indent)
+                     Shape::legacy(self.config.max_width - self.block_indent.width(),
+                                   self.block_indent))
             .unwrap();
         self.buffer.push_str(&rewrite);
         let last = outers.last().unwrap();
@@ -566,12 +566,12 @@ impl<'a> FmtVisitor<'a> {
 }
 
 impl<'a> Rewrite for [ast::Attribute] {
-    fn rewrite(&self, context: &RewriteContext, _: usize, offset: Indent) -> Option<String> {
+    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
         let mut result = String::new();
         if self.is_empty() {
             return Some(result);
         }
-        let indent = offset.to_string(context.config);
+        let indent = shape.indent.to_string(context.config);
 
         for (i, a) in self.iter().enumerate() {
             let mut a_str = context.snippet(a.span);
@@ -587,9 +587,10 @@ impl<'a> Rewrite for [ast::Attribute] {
                 if !comment.is_empty() {
                     let comment = try_opt!(rewrite_comment(comment,
                                                            false,
-                                                           context.config.ideal_width -
-                                                           offset.width(),
-                                                           offset,
+                                                           Shape::legacy(context.config
+                                                                             .ideal_width -
+                                                                         shape.indent.width(),
+                                                                         shape.indent),
                                                            context.config));
                     result.push_str(&indent);
                     result.push_str(&comment);
@@ -603,8 +604,9 @@ impl<'a> Rewrite for [ast::Attribute] {
             if a_str.starts_with("//") {
                 a_str = try_opt!(rewrite_comment(&a_str,
                                                  false,
-                                                 context.config.ideal_width - offset.width(),
-                                                 offset,
+                                                 Shape::legacy(context.config.ideal_width -
+                                                               shape.indent.width(),
+                                                               shape.indent),
                                                  context.config));
             }