about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorNick Cameron <ncameron@mozilla.com>2017-02-21 14:43:43 +1300
committerNick Cameron <ncameron@mozilla.com>2017-02-21 16:35:05 +1300
commitb35906dbce47552c2ba6350f31c654bc77603fbd (patch)
tree793818f31eff2b6083cc391b2aa227293a0e837f /src
parent4f939ddf0c9344da646367d46810eb1ac92c5780 (diff)
WIP
Diffstat (limited to 'src')
-rw-r--r--src/chains.rs72
-rw-r--r--src/expr.rs411
-rw-r--r--src/items.rs55
-rw-r--r--src/lib.rs96
-rw-r--r--src/macros.rs5
-rw-r--r--src/patterns.rs10
-rw-r--r--src/rewrite.rs13
-rw-r--r--src/string.rs5
-rw-r--r--src/types.rs40
-rw-r--r--src/utils.rs6
-rw-r--r--src/visitor.rs5
11 files changed, 382 insertions, 336 deletions
diff --git a/src/chains.rs b/src/chains.rs
index 38f6fad1166..f3cd2ab8f01 100644
--- a/src/chains.rs
+++ b/src/chains.rs
@@ -93,6 +93,7 @@ use syntax::{ast, ptr};
 use syntax::codemap::{mk_sp, Span};
 
 pub fn rewrite_chain(expr: &ast::Expr, context: &RewriteContext, shape: Shape) -> Option<String> {
+    debug!("rewrite_chain {:?}", shape);
     let total_span = expr.span;
     let (parent, subexpr_list) = make_subexpr_list(expr, context);
 
@@ -103,42 +104,45 @@ pub fn rewrite_chain(expr: &ast::Expr, context: &RewriteContext, shape: Shape) -
     }
 
     // Parent is the first item in the chain, e.g., `foo` in `foo.bar.baz()`.
-    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, shape));
+    let mut parent_shape = shape;
+    if is_block_expr(&parent, "\n") {
+        parent_shape = chain_base_indent(context, shape);
+    }
+    let parent_rewrite = try_opt!(parent.rewrite(context, parent_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.
-    let (indent, extend) = if !parent_rewrite.contains('\n') && is_continuable(&parent) ||
-                              parent_rewrite.len() <= context.config.tab_spaces {
-
-        let indent = if let ast::ExprKind::Try(..) = subexpr_list.last().unwrap().node {
-            parent_block_indent.block_indent(context.config)
+    let (nested_shape, extend) = if !parent_rewrite.contains('\n') && is_continuable(&parent) {
+        let nested_shape = if let ast::ExprKind::Try(..) = subexpr_list.last().unwrap().node {
+            parent_shape.block_indent(context.config.tab_spaces)
         } else {
-            chain_indent(context, shape.indent + Indent::new(0, parent_rewrite.len()))
+            chain_indent(context, shape.add_offset(parent_rewrite.len()))
         };
-        (indent, true)
+        (nested_shape, true)
     } else if is_block_expr(&parent, &parent_rewrite) {
         // The parent is a block, so align the rest of the chain with the closing
         // brace.
-        (parent_block_indent, false)
+        (parent_shape, false)
     } else if parent_rewrite.contains('\n') {
-        (chain_indent(context, parent_block_indent.block_indent(context.config)), false)
+        (chain_indent(context, parent_shape.block_indent(context.config.tab_spaces)), false)
     } else {
-        (chain_indent_newline(context, shape.indent + Indent::new(0, parent_rewrite.len())), false)
+        (chain_indent_newline(context, shape.add_offset(parent_rewrite.len())), false)
     };
 
-    let max_width = try_opt!((shape.width + shape.indent.width()).checked_sub(indent.width()));
+    let max_width = try_opt!((shape.width + shape.indent.width() + shape.offset).checked_sub(nested_shape.indent.width() + nested_shape.offset));
+    // The alignement in the shape is only used if we start the item on a new
+    // line, so we don't need to preserve the offset.
+    let child_shape = Shape { width: max_width, ..nested_shape };
+    debug!("child_shape {:?}", child_shape);
     let mut rewrites = try_opt!(subexpr_list.iter()
         .rev()
-        .map(|e| rewrite_chain_subexpr(e, total_span, context, Shape::legacy(max_width, indent)))
+        .map(|e| rewrite_chain_subexpr(e, total_span, context, child_shape))
         .collect::<Option<Vec<_>>>());
 
     // Total of all items excluding the last.
     let almost_total = rewrites[..rewrites.len() - 1]
         .iter()
         .fold(0, |a, b| a + first_line_width(b)) + parent_rewrite.len();
-    let total_width = almost_total + first_line_width(rewrites.last().unwrap());
 
     let veto_single_line = if context.config.take_source_hints && subexpr_list.len() > 1 {
         // Look at the source code. Unless all chain elements start on the same
@@ -152,7 +156,7 @@ pub fn rewrite_chain(expr: &ast::Expr, context: &RewriteContext, shape: Shape) -
         false
     };
 
-    let mut fits_single_line = !veto_single_line && total_width <= shape.width;
+    let mut fits_single_line = !veto_single_line && almost_total <= shape.width;
     if fits_single_line {
         let len = rewrites.len();
         let (init, last) = rewrites.split_at_mut(len - 1);
@@ -178,7 +182,7 @@ pub fn rewrite_chain(expr: &ast::Expr, context: &RewriteContext, shape: Shape) -
         String::new()
     } else {
         // Use new lines.
-        format!("\n{}", indent.to_string(context.config))
+        format!("\n{}", nested_shape.indent.to_string(context.config))
     };
 
     let first_connector = if extend || subexpr_list.is_empty() {
@@ -211,7 +215,7 @@ pub fn rewrite_try(expr: &ast::Expr,
                    context: &RewriteContext,
                    shape: Shape)
                    -> Option<String> {
-    let sub_expr = try_opt!(expr.rewrite(context, shape.sub_width(try_count)));
+    let sub_expr = try_opt!(expr.rewrite(context, try_opt!(shape.sub_width(try_count))));
     Some(format!("{}{}",
                  sub_expr,
                  iter::repeat("?").take(try_count).collect::<String>()))
@@ -268,29 +272,29 @@ fn make_subexpr_list(expr: &ast::Expr, context: &RewriteContext) -> (ast::Expr,
     (parent, subexpr_list)
 }
 
-fn chain_base_indent(context: &RewriteContext, offset: Indent) -> Indent {
+fn chain_base_indent(context: &RewriteContext, shape: Shape) -> Shape {
     match context.config.chain_base_indent {
-        BlockIndentStyle::Visual => offset,
-        BlockIndentStyle::Inherit => context.block_indent,
-        BlockIndentStyle::Tabbed => context.block_indent.block_indent(context.config),
+        BlockIndentStyle::Visual => shape,
+        BlockIndentStyle::Inherit => shape.block_indent(0),
+        BlockIndentStyle::Tabbed => shape.block_indent(context.config.tab_spaces),
     }
 }
 
-fn chain_indent(context: &RewriteContext, offset: Indent) -> Indent {
+fn chain_indent(context: &RewriteContext, shape: Shape) -> Shape {
     match context.config.chain_indent {
-        BlockIndentStyle::Visual => offset,
-        BlockIndentStyle::Inherit => context.block_indent,
-        BlockIndentStyle::Tabbed => context.block_indent.block_indent(context.config),
+        BlockIndentStyle::Visual => shape,
+        BlockIndentStyle::Inherit => shape.block_indent(0),
+        BlockIndentStyle::Tabbed => shape.block_indent(context.config.tab_spaces),
     }
 }
 
 // Ignores visual indenting because this function should be called where it is
 // not possible to use visual indentation because we are starting on a newline.
-fn chain_indent_newline(context: &RewriteContext, _offset: Indent) -> Indent {
+fn chain_indent_newline(context: &RewriteContext, shape: Shape) -> Shape {
     match context.config.chain_indent {
-        BlockIndentStyle::Inherit => context.block_indent,
+        BlockIndentStyle::Inherit => shape.block_indent(0),
         BlockIndentStyle::Visual | BlockIndentStyle::Tabbed => {
-            context.block_indent.block_indent(context.config)
+            shape.block_indent(context.config.tab_spaces)
         }
     }
 }
@@ -303,7 +307,7 @@ fn rewrite_method_call_with_overflow(expr_kind: &ast::ExprKind,
                                      shape: Shape)
                                      -> bool {
     if let &ast::ExprKind::MethodCall(ref method_name, ref types, ref expressions) = expr_kind {
-        let budget = match shape.width.checked_sub(almost_total) {
+        let shape = match shape.shrink_left(almost_total) {
             Some(b) => b,
             None => return false,
         };
@@ -312,8 +316,7 @@ fn rewrite_method_call_with_overflow(expr_kind: &ast::ExprKind,
                                                    expressions,
                                                    total_span,
                                                    context,
-                                                   Shape::legacy(budget,
-                                                                 shape.indent + almost_total));
+                                                   shape);
 
         if let Some(ref mut s) = last_rewrite {
             ::std::mem::swap(s, last);
@@ -362,8 +365,7 @@ fn rewrite_chain_subexpr(expr: &ast::Expr,
                          -> Option<String> {
     match expr.node {
         ast::ExprKind::MethodCall(ref method_name, ref types, ref expressions) => {
-            let inner = &RewriteContext { block_indent: shape.indent, ..*context };
-            rewrite_method_call(method_name.node, types, expressions, span, inner, shape)
+            rewrite_method_call(method_name.node, types, expressions, span, context, shape)
         }
         ast::ExprKind::Field(_, ref field) => {
             let s = format!(".{}", field.node);
diff --git a/src/expr.rs b/src/expr.rs
index 2498ee653af..4f069ba7804 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -253,21 +253,20 @@ pub fn rewrite_pair<LHS, RHS>(lhs: &LHS,
     where LHS: Rewrite,
           RHS: Rewrite
 {
-    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, Shape::legacy(rhs_budget, shape.indent));
+    let rhs_shape = try_opt!(shape.sub_width(suffix.len()));
+    let rhs_result = rhs.rewrite(context, rhs_shape);
 
     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, Shape::legacy(lhs_budget, shape.indent));
+            let lhs_shape = try_opt!(shape.sub_width(prefix.len() + infix.len()));
+            let lhs_result = lhs.rewrite(context, lhs_shape);
             if let Some(lhs_result) = lhs_result {
                 let mut result = format!("{}{}{}", prefix, lhs_result, infix);
 
@@ -281,14 +280,15 @@ 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,
-                                                      Shape::legacy(rhs_budget,
-                                                                    shape.indent + result.len())) {
-                    if rhs_result.len() <= remaining_width {
-                        result.push_str(&rhs_result);
-                        result.push_str(suffix);
-                        return Some(result);
+                let rhs_shape = shape.shrink_left(last_line_width(&result) + suffix.len());
+                if let Some(rhs_shape) = rhs_shape {
+                    if let Some(rhs_result) = rhs.rewrite(context, rhs_shape) {
+                        // FIXME this should always hold.
+                        if rhs_result.len() <= remaining_width {
+                            result.push_str(&rhs_result);
+                            result.push_str(suffix);
+                            return Some(result);
+                        }
                     }
                 }
             }
@@ -301,17 +301,16 @@ pub fn rewrite_pair<LHS, RHS>(lhs: &LHS,
     let infix = infix.trim_right();
     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 = shape.indent + prefix.len();
+        .checked_sub(shape.used_width() + prefix.len() + infix.len()));
+    let rhs_shape = try_opt!(shape.sub_width(suffix.len() + prefix.len())).visual_indent(prefix.len());
 
-    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)));
+    let rhs_result = try_opt!(rhs.rewrite(context, rhs_shape));
+    let lhs_result = try_opt!(lhs.rewrite(context, Shape { width: lhs_budget, ..shape }));
     Some(format!("{}{}{}\n{}{}{}",
                  prefix,
                  lhs_result,
                  infix,
-                 rhs_offset.to_string(context.config),
+                 rhs_shape.indent.to_string(context.config),
                  rhs_result,
                  suffix))
 }
@@ -328,16 +327,14 @@ pub fn rewrite_array<'a, I>(expr_iter: I,
     } else {
         1 // "["
     };
-    let offset = shape.indent + bracket_size;
-    let inner_context = &RewriteContext { block_indent: offset, ..*context };
-    let max_item_width = try_opt!(shape.width.checked_sub(bracket_size * 2));
+    let nested_shape = try_opt!(shape.visual_indent(bracket_size).sub_width(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)),
+                     |item| item.rewrite(context, nested_shape),
                      span.lo,
                      span.hi)
             .collect::<Vec<_>>();
@@ -347,7 +344,7 @@ pub fn rewrite_array<'a, I>(expr_iter: I,
         .fold(Some(false), |acc, x| acc.and_then(|y| x.map(|x| x || y))));
 
     let tactic = if has_long_item || items.iter().any(ListItem::is_multiline) {
-        definitive_tactic(&items, ListTactic::HorizontalVertical, max_item_width)
+        definitive_tactic(&items, ListTactic::HorizontalVertical, nested_shape.width)
     } else {
         DefinitiveListTactic::Mixed
     };
@@ -356,7 +353,7 @@ pub fn rewrite_array<'a, I>(expr_iter: I,
         tactic: tactic,
         separator: ",",
         trailing_separator: SeparatorTactic::Never,
-        shape: Shape::legacy(max_item_width, offset),
+        shape: nested_shape,
         ends_with_newline: false,
         config: context.config,
     };
@@ -390,17 +387,14 @@ fn rewrite_closure(capture: ast::CaptureBy,
     } else {
         ""
     };
-    let offset = shape.indent + mover.len();
-
     // 4 = "|| {".len(), which is overconservative when the closure consists of
     // a single expression.
-    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, Shape::legacy(budget, argument_offset)));
+    let nested_shape = try_opt!(try_opt!(shape.shrink_left(mover.len())).sub_width(4));
 
-    // 1 = space between arguments and return type.
-    let horizontal_budget = budget.checked_sub(ret_str.len() + 1).unwrap_or(0);
+    // 1 = |
+    let argument_offset = nested_shape.indent + 1;
+    let arg_shape = try_opt!(nested_shape.shrink_left(1)).visual_indent(0);
+    let ret_str = try_opt!(fn_decl.output.rewrite(context, arg_shape));
 
     let arg_items =
         itemize_list(context.codemap,
@@ -408,21 +402,23 @@ fn rewrite_closure(capture: ast::CaptureBy,
                      "|",
                      |arg| span_lo_for_arg(arg),
                      |arg| span_hi_for_arg(arg),
-                     |arg| arg.rewrite(context, Shape::legacy(budget, argument_offset)),
+                     |arg| arg.rewrite(context, arg_shape),
                      context.codemap.span_after(span, "|"),
                      body.span.lo);
     let item_vec = arg_items.collect::<Vec<_>>();
+    // 1 = space between arguments and return type.
+    let horizontal_budget = nested_shape.width.checked_sub(ret_str.len() + 1).unwrap_or(0);
     let tactic = definitive_tactic(&item_vec, ListTactic::HorizontalVertical, horizontal_budget);
-    let budget = match tactic {
-        DefinitiveListTactic::Horizontal => horizontal_budget,
-        _ => budget,
+    let arg_shape = match tactic {
+        DefinitiveListTactic::Horizontal => try_opt!(arg_shape.sub_width(ret_str.len() + 1)),
+        _ => arg_shape,
     };
 
     let fmt = ListFormatting {
         tactic: tactic,
         separator: ",",
         trailing_separator: SeparatorTactic::Never,
-        shape: Shape::legacy(budget, argument_offset),
+        shape: arg_shape,
         ends_with_newline: false,
         config: context.config,
     };
@@ -440,12 +436,11 @@ fn rewrite_closure(capture: ast::CaptureBy,
     }
 
     // 1 = space between `|...|` and body.
-    let extra_offset = extra_offset(&prefix, offset) + 1;
-    let budget = try_opt!(shape.width.checked_sub(extra_offset));
-    let total_offset = offset + extra_offset;
+    let extra_offset = extra_offset(&prefix, shape) + 1;
+    let body_shape = try_opt!(shape.sub_width(extra_offset)).add_offset(extra_offset);
 
     if let ast::ExprKind::Block(ref block) = body.node {
-        // The body of the closure is a block.
+        // The body of the closure is an empty block.
         if block.stmts.is_empty() && !block_contains_comment(block, context.codemap) {
             return Some(format!("{} {{}}", prefix));
         }
@@ -461,7 +456,7 @@ fn rewrite_closure(capture: ast::CaptureBy,
                 if let Some(rw) = rewrite_closure_expr(expr,
                                                        &prefix,
                                                        context,
-                                                       Shape::legacy(budget, total_offset)) {
+                                                       body_shape) {
                     return Some(rw);
                 }
             }
@@ -472,8 +467,7 @@ fn rewrite_closure(capture: ast::CaptureBy,
             let stmt = &block.stmts[0];
             // 4 = braces and spaces.
             let mut rewrite = stmt.rewrite(context,
-                                           Shape::legacy(try_opt!(budget.checked_sub(4)),
-                                                         total_offset));
+                                           try_opt!(body_shape.sub_width(4)));
 
             // Checks if rewrite succeeded and fits on a single line.
             rewrite = and_one_line(rewrite);
@@ -484,13 +478,14 @@ fn rewrite_closure(capture: ast::CaptureBy,
         }
 
         // Either we require a block, or tried without and failed.
-        return rewrite_closure_block(&block, prefix, context, budget);
+        let body_shape = shape.block();
+        return rewrite_closure_block(&block, prefix, context, body_shape);
     }
 
     if let Some(rw) = rewrite_closure_expr(body,
                                            &prefix,
                                            context,
-                                           Shape::legacy(budget, total_offset)) {
+                                           body_shape) {
         return Some(rw);
     }
 
@@ -506,7 +501,7 @@ fn rewrite_closure(capture: ast::CaptureBy,
         rules: ast::BlockCheckMode::Default,
         span: body.span,
     };
-    return rewrite_closure_block(&block, prefix, context, budget);
+    return rewrite_closure_block(&block, prefix, context, body_shape.block());
 
     fn rewrite_closure_expr(expr: &ast::Expr,
                             prefix: &str,
@@ -523,11 +518,11 @@ fn rewrite_closure(capture: ast::CaptureBy,
     fn rewrite_closure_block(block: &ast::Block,
                              prefix: String,
                              context: &RewriteContext,
-                             budget: usize)
+                             shape: Shape)
                              -> Option<String> {
         // Start with visual indent, then fall back to block indent if the
         // closure is large.
-        let rewrite = try_opt!(block.rewrite(&context, Shape::legacy(budget, Indent::empty())));
+        let rewrite = try_opt!(block.rewrite(&context, shape));
 
         let block_threshold = context.config.closure_block_indent_threshold;
         if block_threshold < 0 || rewrite.matches('\n').count() <= block_threshold as usize {
@@ -536,9 +531,8 @@ fn rewrite_closure(capture: ast::CaptureBy,
 
         // The body of the closure is big enough to be block indented, that
         // means we must re-format.
-        let mut context = context.clone();
-        context.block_indent.alignment = 0;
-        let rewrite = try_opt!(block.rewrite(&context, Shape::legacy(budget, Indent::empty())));
+        let block_shape = shape.block();
+        let rewrite = try_opt!(block.rewrite(&context, block_shape));
         Some(format!("{} {}", prefix, rewrite))
     }
 }
@@ -581,7 +575,7 @@ impl Rewrite for ast::Block {
         }
 
         let mut visitor = FmtVisitor::from_codemap(context.parse_session, context.config);
-        visitor.block_indent = context.block_indent;
+        visitor.block_indent = shape.indent;
 
         let prefix = match self.rules {
             ast::BlockCheckMode::Unsafe(..) => {
@@ -648,9 +642,7 @@ impl Rewrite for ast::Stmt {
                                 _ => unreachable!(),
                             },
                             context,
-                            Shape::legacy(context.config.max_width - shape.indent.width() -
-                                          suffix.len(),
-                                          shape.indent))
+                            try_opt!(shape.sub_width(suffix.len())))
                     .map(|s| s + suffix)
             }
             ast::StmtKind::Mac(..) |
@@ -815,32 +807,32 @@ impl<'a> ControlFlow<'a> {
 impl<'a> Rewrite for ControlFlow<'a> {
     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
         debug!("ControlFlow::rewrite {:?} {:?}", self, shape);
-        let (budget, indent) = if self.nested_if {
+        let constr_shape = if self.nested_if {
             // We are part of an if-elseif-else chain. Our constraints are tightened.
             // 7 = "} else " .len()
-            (try_opt!(shape.width.checked_sub(7)), shape.indent + 7)
+            try_opt!(shape.shrink_left(7))
         } else {
-            (shape.width, shape.indent)
+            shape
         };
 
         let label_string = rewrite_label(self.label);
         // 1 = space after keyword.
-        let inner_offset = indent + self.keyword.len() + label_string.len() + 1;
-        let mut inner_width =
-            try_opt!(budget.checked_sub(self.keyword.len() + label_string.len() + 1));
-        if context.config.control_brace_style != ControlBraceStyle::AlwaysNextLine {
-            // 2 = " {".len()
-            inner_width = try_opt!(inner_width.checked_sub(2));
-        }
+        let add_offset = self.keyword.len() + label_string.len() + 1;
 
         let pat_expr_string = match self.cond {
             Some(cond) => {
+                let mut cond_shape = try_opt!(constr_shape.shrink_left(add_offset));
+                if context.config.control_brace_style != ControlBraceStyle::AlwaysNextLine {
+                    // 2 = " {".len()
+                    cond_shape = try_opt!(cond_shape.sub_width(2));
+                }
+
                 try_opt!(rewrite_pat_expr(context,
                                           self.pat,
                                           cond,
                                           self.matcher,
                                           self.connector,
-                                          Shape::legacy(inner_width, inner_offset)))
+                                          cond_shape))
             }
             None => String::new(),
         };
@@ -855,21 +847,24 @@ impl<'a> Rewrite for ControlFlow<'a> {
             }
         }
 
+        let used_width = if pat_expr_string.contains('\n') {
+            last_line_width(&pat_expr_string)
+        } else {
+            // 2 = spaces after keyword and condition.
+            label_string.len() + self.keyword.len() + pat_expr_string.len() + 2
+        };
+        let block_width = try_opt!(shape.width.checked_sub(used_width));
         // 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!(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, Shape::legacy(block_width, shape.indent)));
+        // TODO this .block() - not what we want if we are actually visually indented
+        let block_shape = Shape { width: block_width, ..shape };
+        let block_str = try_opt!(self.block.rewrite(context, block_shape));
 
         let cond_span = if let Some(cond) = self.cond {
             cond.span
@@ -891,7 +886,7 @@ impl<'a> Rewrite for ControlFlow<'a> {
         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 alt_block_sep = String::from("\n") + &shape.indent.block_only().to_string(context.config);
         let block_sep = if self.cond.is_none() && between_kwd_cond_comment.is_some() {
             ""
         } else if context.config.control_brace_style ==
@@ -913,14 +908,8 @@ impl<'a> Rewrite for ControlFlow<'a> {
 
         if let Some(else_block) = self.else_block {
             // Since this is an else block, we should not indent for the assignment preceding
-            // the original if, so set shape.indent.alignment to 0.
-            let shape = Shape {
-                width: shape.width,
-                indent: Indent {
-                    block_indent: shape.indent.block_indent,
-                    alignment: 0,
-                },
-            };
+            // the original if, so set shape.offset to 0.
+            let shape = Shape { offset: 0, ..shape };
             let mut last_in_chain = false;
             let rewrite = match else_block.node {
                 // If the else expression is another if-else expression, prevent it
@@ -935,7 +924,7 @@ impl<'a> Rewrite for ControlFlow<'a> {
                                         false,
                                         true,
                                         mk_sp(else_block.span.lo, self.span.hi))
-                        .rewrite(context, shape)
+                        .rewrite(context, shape.visual_indent(0))
                 }
                 ast::ExprKind::If(ref cond, ref if_block, ref next_else_block) => {
                     ControlFlow::new_if(cond,
@@ -945,13 +934,14 @@ impl<'a> Rewrite for ControlFlow<'a> {
                                         false,
                                         true,
                                         mk_sp(else_block.span.lo, self.span.hi))
-                        .rewrite(context, shape)
+                        .rewrite(context, shape.visual_indent(0))
                 }
                 _ => {
                     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, Shape::legacy(min(1, shape.width), shape.indent))
+                    let else_shape = Shape { width: min(1, shape.width), ..shape };
+                    else_block.rewrite(context, else_shape)
                 }
             };
 
@@ -1097,16 +1087,15 @@ fn rewrite_match(context: &RewriteContext,
     // `match `cond` {`
     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 alt_block_sep = String::from("\n") + &shape.indent.block_only().to_string(context.config);
     let block_sep = match context.config.control_brace_style {
         ControlBraceStyle::AlwaysSameLine => " ",
         _ => alt_block_sep.as_str(),
     };
     let mut result = format!("match {}{}{{", cond_str, block_sep);
 
-    let nested_context = context.nested_context();
-    let arm_indent = nested_context.block_indent;
-    let arm_indent_str = arm_indent.to_string(context.config);
+    let arm_shape = shape.block_indent(context.config.tab_spaces);
+    let arm_indent_str = arm_shape.indent.to_string(context.config);
 
     let open_brace_pos = context.codemap
         .span_after(mk_sp(cond.span.hi, arm_start_pos(&arms[0])), "{");
@@ -1120,15 +1109,14 @@ fn rewrite_match(context: &RewriteContext,
         };
         let comment = try_opt!(rewrite_match_arm_comment(context,
                                                          &missed_str,
-                                                         Shape::legacy(shape.width, arm_indent),
+                                                         arm_shape,
                                                          &arm_indent_str));
         result.push_str(&comment);
         result.push('\n');
         result.push_str(&arm_indent_str);
 
-        let arm_str = arm.rewrite(&nested_context,
-                                  Shape::legacy(context.config.max_width - arm_indent.width(),
-                                                arm_indent));
+        let arm_str = arm.rewrite(&context,
+                                  Shape { width: context.config.max_width - arm_shape.indent.width(), .. arm_shape });
         if let Some(ref arm_str) = arm_str {
             result.push_str(arm_str);
         } else {
@@ -1143,11 +1131,11 @@ fn rewrite_match(context: &RewriteContext,
     let last_comment = context.snippet(last_span);
     let comment = try_opt!(rewrite_match_arm_comment(context,
                                                      &last_comment,
-                                                     Shape::legacy(shape.width, arm_indent),
+                                                     arm_shape,
                                                      &arm_indent_str));
     result.push_str(&comment);
     result.push('\n');
-    result.push_str(&context.block_indent.to_string(context.config));
+    result.push_str(&shape.indent.to_string(context.config));
     result.push('}');
     Some(result)
 }
@@ -1197,7 +1185,7 @@ impl Rewrite for ast::Arm {
             // We only use this visitor for the attributes, should we use it for
             // more?
             let mut attr_visitor = FmtVisitor::from_codemap(context.parse_session, context.config);
-            attr_visitor.block_indent = context.block_indent;
+            attr_visitor.block_indent = shape.indent.block_only();
             attr_visitor.last_pos = attrs[0].span.lo;
             if attr_visitor.visit_attrs(attrs) {
                 // Attributes included a skip instruction.
@@ -1211,9 +1199,10 @@ impl Rewrite for ast::Arm {
 
         // Patterns
         // 5 = ` => {`
-        let pat_budget = try_opt!(shape.width.checked_sub(5));
+        let pat_shape = try_opt!(shape.sub_width(5));
+
         let pat_strs = try_opt!(pats.iter()
-            .map(|p| p.rewrite(context, Shape::legacy(pat_budget, shape.indent)))
+            .map(|p| p.rewrite(context, pat_shape))
             .collect::<Option<Vec<_>>>());
 
         let all_simple = pat_strs.iter().all(|p| pat_is_simple(p));
@@ -1226,29 +1215,26 @@ impl Rewrite for ast::Arm {
             },
             separator: " |",
             trailing_separator: SeparatorTactic::Never,
-            shape: Shape::legacy(pat_budget, shape.indent),
+            shape: pat_shape,
             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 - shape.indent.width()
+        let guard_shape = if pats_str.contains('\n') {
+            Shape { width: context.config.max_width - shape.indent.width(), ..shape }
         } else {
-            shape.width
+            shape
         };
 
         let guard_str = try_opt!(rewrite_guard(context,
                                                guard,
-                                               Shape::legacy(budget, shape.indent),
+                                               guard_shape,
                                                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 += shape.indent.width();
-        }
+        let mut line_start = trimmed_last_line_width(&pats_str);
 
         let body = match body.node {
             ast::ExprKind::Block(ref block) if !is_unsafe_block(block) &&
@@ -1264,16 +1250,17 @@ impl Rewrite for ast::Arm {
         };
 
         let comma = arm_comma(&context.config, self, body);
-        let alt_block_sep = String::from("\n") + &context.block_indent.to_string(context.config);
+        let alt_block_sep = String::from("\n") + &shape.indent.block_only().to_string(context.config);
 
         // Let's try and get the arm body on the same line as the condition.
         // 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(shape.indent.block_indent,
-                                     line_start + 4 - shape.indent.block_indent);
-            let rewrite = nop_block_collapse(body.rewrite(context, Shape::legacy(budget, offset)),
-                                             budget);
+        if shape.width > line_start + comma.len() + 4 {
+            let arm_shape = shape.shrink_left(line_start + 4).unwrap().sub_width(comma.len()).unwrap().block();
+            // TODO
+            // let offset = Indent::new(shape.indent.block_indent,
+            //                          line_start + 4 - shape.indent.block_indent);
+            let rewrite = nop_block_collapse(body.rewrite(context, arm_shape),
+                                             arm_shape.width);
             let is_block = if let ast::ExprKind::Block(..) = body.node {
                 true
             } else {
@@ -1301,13 +1288,12 @@ 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!(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,
-                                                                      Shape::legacy(body_budget,
-                                                                                    indent)),
-                                                         body_budget));
+        // TODO
+        // let body_budget = try_opt!(shape.width.checked_sub(context.config.tab_spaces));
+        // let indent = shape.indent.block_only().block_indent(context.config);
+        let body_shape = try_opt!(shape.sub_width(context.config.tab_spaces)).block_indent(context.config.tab_spaces);
+        let next_line_body = try_opt!(nop_block_collapse(body.rewrite(context, body_shape),
+                                                         body_shape.width));
         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 {
@@ -1394,6 +1380,7 @@ fn rewrite_pat_expr(context: &RewriteContext,
                     connector: &str,
                     shape: Shape)
                     -> Option<String> {
+    debug!("rewrite_pat_expr {:?}", shape);
     let mut result = match pat {
         Some(pat) => {
             let matcher = if matcher.is_empty() {
@@ -1401,26 +1388,22 @@ fn rewrite_pat_expr(context: &RewriteContext,
             } else {
                 format!("{} ", matcher)
             };
-            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)));
+            let pat_shape = try_opt!(try_opt!(shape.shrink_left(matcher.len())).sub_width(connector.len()));
+            let pat_string = try_opt!(pat.rewrite(context, pat_shape));
             format!("{}{}{}", matcher, pat_string, connector)
         }
         None => String::new(),
     };
 
     // Consider only the last line of the pat string.
-    let extra_offset = extra_offset(&result, shape.indent);
+    let extra_offset = extra_offset(&result, shape);
 
-    // The expression may (partionally) fit on the current line.
+    // The expression may (partially) fit on the current line.
     if shape.width > extra_offset + 1 {
         let spacer = if pat.is_some() { " " } else { "" };
 
-        let expr_rewrite = expr.rewrite(context,
-                                        Shape::legacy(try_opt!(shape.width
-                                                          .checked_sub(extra_offset +
-                                                                       spacer.len())),
-                                                      shape.indent + extra_offset + spacer.len()));
+        let expr_shape = try_opt!(shape.sub_width(extra_offset + spacer.len())).add_offset(extra_offset + spacer.len());
+        let expr_rewrite = expr.rewrite(context, expr_shape);
 
         if let Some(expr_string) = expr_rewrite {
             let pat_simple = pat.and_then(|p| {
@@ -1437,17 +1420,17 @@ fn rewrite_pat_expr(context: &RewriteContext,
         }
     }
 
-    let nested = context.nested_context();
+    let nested_indent = shape.indent.block_only().block_indent(context.config);
 
     // The expression won't fit on the current line, jump to next.
     result.push('\n');
-    result.push_str(&nested.block_indent.to_string(context.config));
+    result.push_str(&nested_indent.to_string(context.config));
 
-    let expr_rewrite = expr.rewrite(&nested,
+    let expr_rewrite = expr.rewrite(&context,
                                     Shape::legacy(try_opt!(context.config
                                                       .max_width
-                                                      .checked_sub(nested.block_indent.width())),
-                                                  nested.block_indent));
+                                                      .checked_sub(nested_indent.width())),
+                                                  nested_indent));
     result.push_str(&try_opt!(expr_rewrite));
 
     Some(result)
@@ -1533,7 +1516,7 @@ fn rewrite_call_inner<R>(context: &RewriteContext,
     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, Shape::legacy(max_callee_width, shape.indent)) {
+    let callee_str = match callee.rewrite(context, Shape { width: max_callee_width, ..shape }) {
         Some(string) => {
             if !string.contains('\n') && string.len() > max_callee_width {
                 panic!("{:?} {}", string, max_callee_width);
@@ -1547,20 +1530,15 @@ 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, shape.indent);
+    let used_width = extra_offset(&callee_str, shape);
     // 2 is for parens.
-    let remaining_width = match shape.width.checked_sub(extra_offset + 2) {
-        Some(str) => str,
+    let remaining_width = match shape.width.checked_sub(used_width + 2) {
+        Some(s) => s,
         None => return Err(Ordering::Greater),
     };
-    let offset = shape.indent + extra_offset + 1;
+    // 1 = (
+    let nested_shape = shape.visual_indent(used_width + 1);
     let arg_count = args.len();
-    let block_indent = if arg_count == 1 {
-        context.block_indent
-    } else {
-        offset
-    };
-    let inner_context = &RewriteContext { block_indent: block_indent, ..*context };
 
     let items =
         itemize_list(context.codemap,
@@ -1568,7 +1546,7 @@ fn rewrite_call_inner<R>(context: &RewriteContext,
                      ")",
                      |item| item.span.lo,
                      |item| item.span.hi,
-                     |item| item.rewrite(inner_context, Shape::legacy(remaining_width, offset)),
+                     |item| item.rewrite(context, Shape { width: remaining_width, ..nested_shape }),
                      span.lo,
                      span.hi);
     let mut item_vec: Vec<_> = items.collect();
@@ -1588,9 +1566,12 @@ fn rewrite_call_inner<R>(context: &RewriteContext,
     // Replace the last item with its first line to see if it fits with
     // first arguments.
     if overflow_last {
-        let inner_context = &RewriteContext { block_indent: context.block_indent, ..*context };
-        let rewrite =
-            args.last().unwrap().rewrite(inner_context, Shape::legacy(remaining_width, offset));
+        let nested_shape = Shape {
+            width: remaining_width,
+            indent: nested_shape.indent.block_only(),
+            ..nested_shape
+        };
+        let rewrite = args.last().unwrap().rewrite(context, nested_shape);
 
         if let Some(rewrite) = rewrite {
             let rewrite_first_line = Some(rewrite[..first_line_width(&rewrite)].to_owned());
@@ -1622,7 +1603,7 @@ fn rewrite_call_inner<R>(context: &RewriteContext,
         tactic: tactic,
         separator: ",",
         trailing_separator: SeparatorTactic::Never,
-        shape: Shape::legacy(shape.width, offset),
+        shape: nested_shape,
         ends_with_newline: false,
         config: context.config,
     };
@@ -1643,9 +1624,8 @@ fn rewrite_paren(context: &RewriteContext, subexpr: &ast::Expr, shape: Shape) ->
     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,
-                                      Shape::legacy(try_opt!(shape.width.checked_sub(2)),
-                                                    shape.indent + 1));
+    let sub_shape = try_opt!(shape.sub_width(2)).visual_indent(1);
+    let subexpr_str = subexpr.rewrite(context, sub_shape);
     debug!("rewrite_paren, subexpr_str: `{:?}`", subexpr_str);
 
     subexpr_str.map(|s| if context.config.spaces_within_parens && s.len() > 0 {
@@ -1699,24 +1679,20 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
     }
 
     // 2 = " {".len()
-    let path_budget = try_opt!(shape.width.checked_sub(2));
+    let path_shape = try_opt!(shape.sub_width(2));
     let path_str = try_opt!(rewrite_path(context,
                                          PathContext::Expr,
                                          None,
                                          path,
-                                         Shape::legacy(path_budget, shape.indent)));
+                                         path_shape));
 
     // Foo { a: Foo } - indent is +3, width is -5.
-    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 => (shape.indent + path_str.len() + 3, h_budget),
+    let h_shape = shape.sub_width(path_str.len() + 5);
+    let v_shape = match context.config.struct_lit_style {
+        StructLitStyle::Visual => try_opt!(try_opt!(shape.shrink_left(path_str.len() + 3)).sub_width(2)),
         StructLitStyle::Block => {
-            // If we are all on one line, then we'll ignore the indent, and we
-            // have a smaller budget.
-            let indent = context.block_indent.block_indent(context.config);
-            let v_budget = context.config.max_width.checked_sub(indent.width()).unwrap_or(0);
-            (indent, v_budget)
+            let shape = shape.block_indent(context.config.tab_spaces);
+            Shape { width: try_opt!(context.config.max_width.checked_sub(shape.indent.width())), ..shape }
         }
     };
 
@@ -1724,8 +1700,6 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
         .map(StructLitField::Regular)
         .chain(base.into_iter().map(StructLitField::Base));
 
-    let inner_context = &RewriteContext { block_indent: indent, ..*context };
-
     let items = itemize_list(context.codemap,
                              field_iter,
                              "}",
@@ -1747,14 +1721,15 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
                              |item| {
         match *item {
             StructLitField::Regular(field) => {
-                rewrite_field(inner_context,
+                // The 1 taken from the v_budget is for the comma.
+                rewrite_field(context,
                               field,
-                              Shape::legacy(v_budget.checked_sub(1).unwrap_or(0), indent))
+                              try_opt!(v_shape.sub_width(1)))
             }
             StructLitField::Base(expr) => {
                 // 2 = ..
-                expr.rewrite(inner_context,
-                             Shape::legacy(try_opt!(v_budget.checked_sub(2)), indent + 2))
+                expr.rewrite(context,
+                             try_opt!(v_shape.shrink_left(2)))
                     .map(|s| format!("..{}", s))
             }
         }
@@ -1763,7 +1738,7 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
                              span.hi);
     let item_vec = items.collect::<Vec<_>>();
 
-    let tactic = {
+    let tactic = if let Some(h_shape) = h_shape {
         let mut prelim_tactic = match (context.config.struct_lit_style, fields.len()) {
             (StructLitStyle::Visual, 1) => ListTactic::HorizontalVertical,
             _ => context.config.struct_lit_multiline_style.to_list_tactic(),
@@ -1773,12 +1748,14 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
             prelim_tactic = ListTactic::LimitedHorizontalVertical(context.config.struct_lit_width);
         }
 
-        definitive_tactic(&item_vec, prelim_tactic, h_budget)
+        definitive_tactic(&item_vec, prelim_tactic, h_shape.width)
+    } else {
+        DefinitiveListTactic::Vertical
     };
 
-    let budget = match tactic {
-        DefinitiveListTactic::Horizontal => h_budget,
-        _ => v_budget,
+    let nested_shape = match tactic {
+        DefinitiveListTactic::Horizontal => h_shape.unwrap(),
+        _ => v_shape,
     };
 
     let ends_with_newline = context.config.struct_lit_style != StructLitStyle::Visual &&
@@ -1792,36 +1769,33 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
         } else {
             context.config.struct_lit_trailing_comma
         },
-        shape: Shape::legacy(budget, indent),
+        shape: nested_shape,
         ends_with_newline: ends_with_newline,
         config: context.config,
     };
     let fields_str = try_opt!(write_list(&item_vec, &fmt));
 
+    // Empty struct.
     if fields_str.is_empty() {
         return Some(format!("{} {{}}", path_str));
     }
 
-    let format_on_newline = || {
-        let inner_indent = context.block_indent
-            .block_indent(context.config)
-            .to_string(context.config);
-        let outer_indent = context.block_indent.to_string(context.config);
-        Some(format!("{} {{\n{}{}\n{}}}",
-                     path_str,
-                     inner_indent,
-                     fields_str,
-                     outer_indent))
-    };
-
-    match (context.config.struct_lit_style, context.config.struct_lit_multiline_style) {
-        (StructLitStyle::Block, _) if fields_str.contains('\n') || fields_str.len() > h_budget => {
-            format_on_newline()
-        }
-        (StructLitStyle::Block, MultilineStyle::ForceMulti) => format_on_newline(),
-        _ => Some(format!("{} {{ {} }}", path_str, fields_str)),
+    // One liner or visual indent.
+    if context.config.struct_lit_style == StructLitStyle::Visual ||
+       (context.config.struct_lit_multiline_style != MultilineStyle::ForceMulti &&
+        !fields_str.contains('\n') &&
+        fields_str.len() <= h_shape.map(|s| s.width).unwrap_or(0)) {
+        return Some(format!("{} {{ {} }}", path_str, fields_str));
     }
 
+    // Multiple lines.
+    let inner_indent = v_shape.indent.to_string(context.config);
+    let outer_indent = shape.indent.to_string(context.config);
+    Some(format!("{} {{\n{}{}\n{}}}",
+                 path_str,
+                 inner_indent,
+                 fields_str,
+                 outer_indent))
     // FIXME if context.config.struct_lit_style == Visual, but we run out
     // of space, we should fall back to BlockIndent.
 }
@@ -1842,9 +1816,9 @@ fn rewrite_field(context: &RewriteContext, field: &ast::Field, shape: Shape) ->
     } else {
         let separator = type_annotation_separator(context.config);
         let overhead = name.len() + separator.len();
-        let expr = field.expr.rewrite(context,
-                                      Shape::legacy(try_opt!(shape.width.checked_sub(overhead)),
-                                                    shape.indent + overhead));
+        let mut expr_shape = try_opt!(shape.sub_width(overhead));
+        expr_shape.offset += overhead;
+        let expr = field.expr.rewrite(context, expr_shape);
 
         match expr {
             Some(e) => Some(format!("{}{}{}", name, separator, e)),
@@ -1871,16 +1845,14 @@ pub fn rewrite_tuple<'a, I>(context: &RewriteContext,
           <I as Iterator>::Item: Deref,
           <I::Item as Deref>::Target: Rewrite + Spanned + 'a
 {
-    let indent = shape.indent + 1;
-    let aligned = RewriteContext { block_indent: indent, ..context.clone() };
-
+    debug!("rewrite_tuple {:?}", shape);
     // In case of length 1, need a trailing comma
     if items.len() == 1 {
         // 3 = "(" + ",)"
-        let budget = try_opt!(shape.width.checked_sub(3));
+        let nested_shape = try_opt!(shape.sub_width(3)).visual_indent(1);
         return items.next()
             .unwrap()
-            .rewrite(&aligned, Shape::legacy(budget, indent))
+            .rewrite(context, nested_shape)
             .map(|s| if context.config.spaces_within_parens {
                 format!("( {}, )", s)
             } else {
@@ -1889,16 +1861,16 @@ pub fn rewrite_tuple<'a, I>(context: &RewriteContext,
     }
 
     let list_lo = context.codemap.span_after(span, "(");
-    let budget = try_opt!(shape.width.checked_sub(2));
+    let nested_shape = try_opt!(shape.sub_width(2)).visual_indent(1);
     let items = itemize_list(context.codemap,
                              items,
                              ")",
                              |item| item.span().lo,
                              |item| item.span().hi,
-                             |item| item.rewrite(&aligned, Shape::legacy(budget, indent)),
+                             |item| item.rewrite(context, nested_shape),
                              list_lo,
                              span.hi - BytePos(1));
-    let list_str = try_opt!(format_item_list(items, Shape::legacy(budget, indent), context.config));
+    let list_str = try_opt!(format_item_list(items, nested_shape, context.config));
 
     if context.config.spaces_within_parens && list_str.len() > 0 {
         Some(format!("( {} )", list_str))
@@ -1912,10 +1884,8 @@ pub fn rewrite_unary_prefix<R: Rewrite>(context: &RewriteContext,
                                         rewrite: &R,
                                         shape: Shape)
                                         -> Option<String> {
-    rewrite.rewrite(context,
-                 Shape::legacy(try_opt!(shape.width.checked_sub(prefix.len())),
-                               shape.indent + prefix.len()))
-        .map(|r| format!("{}{}", prefix, r))
+    let shape = try_opt!(shape.shrink_left(prefix.len())).visual_indent(0);
+    rewrite.rewrite(context, shape).map(|r| format!("{}{}", prefix, r))
 }
 
 // FIXME: this is probably not correct for multi-line Rewrites. we should
@@ -1925,9 +1895,7 @@ pub fn rewrite_unary_suffix<R: Rewrite>(context: &RewriteContext,
                                         rewrite: &R,
                                         shape: Shape)
                                         -> Option<String> {
-    rewrite.rewrite(context,
-                 Shape::legacy(try_opt!(shape.width.checked_sub(suffix.len())),
-                               shape.indent))
+    rewrite.rewrite(context, try_opt!(shape.sub_width(suffix.len())))
         .map(|mut r| {
             r.push_str(suffix);
             r
@@ -1960,9 +1928,9 @@ fn rewrite_assignment(context: &RewriteContext,
     };
 
     // 1 = space between lhs and operator.
-    let max_width = try_opt!(shape.width.checked_sub(operator_str.len() + 1));
+    let lhs_shape = try_opt!(shape.sub_width(operator_str.len() + 1));
     let lhs_str = format!("{} {}",
-                          try_opt!(lhs.rewrite(context, Shape::legacy(max_width, shape.indent))),
+                          try_opt!(lhs.rewrite(context, lhs_shape)),
                           operator_str);
 
     rewrite_assign_rhs(context, lhs_str, rhs, shape)
@@ -1985,7 +1953,9 @@ pub fn rewrite_assign_rhs<S: Into<String>>(context: &RewriteContext,
     // 1 = space between operator and rhs.
     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));
+                         Shape::offset(max_width,
+                                       shape.indent.block_only(),
+                                       shape.indent.alignment + last_line_width + 1));
 
     fn count_line_breaks(src: &str) -> usize {
         src.chars().filter(|&x| x == '\n').count()
@@ -2003,8 +1973,7 @@ pub fn rewrite_assign_rhs<S: Into<String>>(context: &RewriteContext,
             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, Shape::legacy(max_width, new_offset));
+            let new_rhs = ex.rewrite(context, Shape::legacy(max_width, new_offset));
 
             // FIXME: DRY!
             match (rhs, new_rhs) {
diff --git a/src/items.rs b/src/items.rs
index 3c5bf3ce6ce..1a27b5bfbc9 100644
--- a/src/items.rs
+++ b/src/items.rs
@@ -70,12 +70,12 @@ impl Rewrite for ast::Local {
 
         if let Some(ref ex) = self.init {
             // 1 = trailing semicolon;
-            let budget = try_opt!(shape.width.checked_sub(context.block_indent.width() + 1));
+            let budget = try_opt!(shape.width.checked_sub(shape.indent.block_only().width() + 1));
 
             result = try_opt!(rewrite_assign_rhs(&context,
                                                  result,
                                                  ex,
-                                                 Shape::legacy(budget, context.block_indent)));
+                                                 Shape::legacy(budget, shape.indent.block_only())));
         }
 
         result.push(';');
@@ -520,7 +520,7 @@ pub fn format_impl(context: &RewriteContext, item: &ast::Item, offset: Indent) -
                                                              context.config,
                                                              context.config.item_brace_style,
                                                              Shape::legacy(where_budget,
-                                                                           context.block_indent),
+                                                                           offset.block_only()),
                                                              context.config.where_density,
                                                              "{",
                                                              true,
@@ -539,7 +539,7 @@ pub fn format_impl(context: &RewriteContext, item: &ast::Item, offset: Indent) -
 
         if !where_clause_str.is_empty() && !where_clause_str.contains('\n') {
             result.push('\n');
-            let width = context.block_indent.width() + context.config.tab_spaces - 1;
+            let width = offset.block_indent + context.config.tab_spaces - 1;
             let where_indent = Indent::new(0, width);
             result.push_str(&where_indent.to_string(context.config));
         }
@@ -568,7 +568,7 @@ pub fn format_impl(context: &RewriteContext, item: &ast::Item, offset: Indent) -
 
         if !items.is_empty() || contains_comment(&snippet[open_pos..]) {
             let mut visitor = FmtVisitor::from_codemap(context.parse_session, context.config);
-            visitor.block_indent = context.block_indent.block_indent(context.config);
+            visitor.block_indent = offset.block_only().block_indent(context.config);
             visitor.last_pos = item.span.lo + BytePos(open_pos as u32);
 
             for item in items {
@@ -578,7 +578,7 @@ pub fn format_impl(context: &RewriteContext, item: &ast::Item, offset: Indent) -
             visitor.format_missing(item.span.hi - BytePos(1));
 
             let inner_indent_str = visitor.block_indent.to_string(context.config);
-            let outer_indent_str = context.block_indent.to_string(context.config);
+            let outer_indent_str = offset.block_only().to_string(context.config);
 
             result.push('\n');
             result.push_str(&inner_indent_str);
@@ -654,7 +654,7 @@ fn format_impl_ref_and_type(context: &RewriteContext,
                 result.push('\n');
 
                 // Add indentation of one additional tab.
-                let width = context.block_indent.width() + context.config.tab_spaces;
+                let width = offset.block_indent + context.config.tab_spaces;
                 let for_indent = Indent::new(0, width);
                 result.push_str(&for_indent.to_string(context.config));
 
@@ -762,7 +762,7 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent)
         if offset.width() + last_line_width(&result) + trait_bound_str.len() >
            context.config.ideal_width {
             result.push('\n');
-            let trait_indent = context.block_indent.block_indent(context.config);
+            let trait_indent = offset.block_only().block_indent(context.config);
             result.push_str(&trait_indent.to_string(context.config));
         }
         result.push_str(&trait_bound_str);
@@ -789,7 +789,7 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent)
                                                              context.config,
                                                              context.config.item_brace_style,
                                                              Shape::legacy(where_budget,
-                                                                           context.block_indent),
+                                                                           offset.block_only()),
                                                              where_density,
                                                              "{",
                                                              has_body,
@@ -800,7 +800,7 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent)
            last_line_width(&result) + where_clause_str.len() + offset.width() >
            context.config.ideal_width {
             result.push('\n');
-            let width = context.block_indent.width() + context.config.tab_spaces - 1;
+            let width = offset.block_indent + context.config.tab_spaces - 1;
             let where_indent = Indent::new(0, width);
             result.push_str(&where_indent.to_string(context.config));
         }
@@ -829,7 +829,7 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent)
 
         if !trait_items.is_empty() || contains_comment(&snippet[open_pos..]) {
             let mut visitor = FmtVisitor::from_codemap(context.parse_session, context.config);
-            visitor.block_indent = context.block_indent.block_indent(context.config);
+            visitor.block_indent = offset.block_only().block_indent(context.config);
             visitor.last_pos = item.span.lo + BytePos(open_pos as u32);
 
             for item in trait_items {
@@ -839,7 +839,7 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent)
             visitor.format_missing(item.span.hi - BytePos(1));
 
             let inner_indent_str = visitor.block_indent.to_string(context.config);
-            let outer_indent_str = context.block_indent.to_string(context.config);
+            let outer_indent_str = offset.block_only().to_string(context.config);
 
             result.push('\n');
             result.push_str(&inner_indent_str);
@@ -892,7 +892,7 @@ fn format_struct_struct(context: &RewriteContext,
         }
         None => {
             if context.config.item_brace_style == BraceStyle::AlwaysNextLine && !fields.is_empty() {
-                format!("\n{}{{", context.block_indent.to_string(context.config))
+                format!("\n{}{{", offset.block_only().to_string(context.config))
             } else {
                 " {".to_owned()
             }
@@ -1004,7 +1004,7 @@ fn format_tuple_struct(context: &RewriteContext,
                                           &generics.where_clause,
                                           context.config,
                                           context.config.item_brace_style,
-                                          Shape::legacy(where_budget, context.block_indent),
+                                          Shape::legacy(where_budget, offset.block_only()),
                                           Density::Compressed,
                                           ";",
                                           false,
@@ -1014,7 +1014,7 @@ fn format_tuple_struct(context: &RewriteContext,
     };
     result.push('(');
 
-    let item_indent = context.block_indent + result.len();
+    let item_indent = offset.block_only() + result.len();
     // 2 = ");"
     let item_budget = try_opt!(context.config.max_width.checked_sub(item_indent.width() + 2));
 
@@ -1052,12 +1052,12 @@ fn format_tuple_struct(context: &RewriteContext,
 
     if !where_clause_str.is_empty() && !where_clause_str.contains('\n') &&
        (result.contains('\n') ||
-        context.block_indent.width() + result.len() + where_clause_str.len() + 1 >
+        offset.block_indent + result.len() + where_clause_str.len() + 1 >
         context.config.max_width) {
         // We need to put the where clause on a new line, but we didn'to_string
         // know that earlier, so the where clause will not be indented properly.
         result.push('\n');
-        result.push_str(&(context.block_indent + (context.config.tab_spaces - 1))
+        result.push_str(&(offset.block_only() + (context.config.tab_spaces - 1))
             .to_string(context.config));
     }
     result.push_str(&where_clause_str);
@@ -1192,6 +1192,7 @@ pub fn rewrite_static(prefix: &str,
                       ty: &ast::Ty,
                       mutability: ast::Mutability,
                       expr_opt: Option<&ptr::P<ast::Expr>>,
+                      offset: Indent,
                       context: &RewriteContext)
                       -> Option<String> {
     let type_annotation_spacing = type_annotation_spacing(context.config);
@@ -1205,19 +1206,19 @@ pub fn rewrite_static(prefix: &str,
     // 2 = " =".len()
     let ty_str = try_opt!(ty.rewrite(context,
                                      Shape::legacy(context.config.max_width -
-                                                   context.block_indent.width() -
+                                                   offset.block_indent -
                                                    prefix.len() -
                                                    2,
-                                                   context.block_indent)));
+                                                   offset.block_only())));
 
     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;
+        let remaining_width = context.config.max_width - offset.block_indent - 1;
         rewrite_assign_rhs(context,
                            lhs,
                            expr,
-                           Shape::legacy(remaining_width, context.block_indent))
+                           Shape::legacy(remaining_width, offset.block_only()))
             .map(|s| s + ";")
     } else {
         let lhs = format!("{}{};", prefix, ty_str);
@@ -1253,10 +1254,10 @@ pub fn rewrite_associated_type(ident: ast::Ident,
     if let Some(ty) = ty_opt {
         let ty_str = try_opt!(ty.rewrite(context,
                                          Shape::legacy(context.config.max_width -
-                                                       context.block_indent.width() -
+                                                       indent.block_indent -
                                                        prefix.len() -
                                                        2,
-                                                       context.block_indent)));
+                                                       indent.block_only())));
         Some(format!("{} = {};", prefix, ty_str))
     } else {
         Some(format!("{}{};", prefix, type_bounds_str))
@@ -1473,7 +1474,7 @@ fn rewrite_fn_base(context: &RewriteContext,
         multi_line_budget = context.config.max_width - arg_indent.width();
     }
 
-    debug!("rewrite_fn: one_line_budget: {}, multi_line_budget: {}, arg_indent: {:?}",
+    debug!("rewrite_fn_base: one_line_budget: {}, multi_line_budget: {}, arg_indent: {:?}",
            one_line_budget,
            multi_line_budget,
            arg_indent);
@@ -2020,7 +2021,7 @@ fn format_generics(context: &RewriteContext,
                                                              context.config,
                                                              brace_style,
                                                              Shape::legacy(budget,
-                                                                           context.block_indent),
+                                                                           offset.block_only()),
                                                              Density::Tall,
                                                              terminator,
                                                              true,
@@ -2029,7 +2030,7 @@ fn format_generics(context: &RewriteContext,
         if !force_same_line_brace &&
            (brace_style == BraceStyle::SameLineWhere || brace_style == BraceStyle::AlwaysNextLine) {
             result.push('\n');
-            result.push_str(&context.block_indent.to_string(context.config));
+            result.push_str(&offset.block_only().to_string(context.config));
         } else {
             result.push(' ');
         }
@@ -2037,7 +2038,7 @@ fn format_generics(context: &RewriteContext,
     } else {
         if !force_same_line_brace && brace_style == BraceStyle::AlwaysNextLine {
             result.push('\n');
-            result.push_str(&context.block_indent.to_string(context.config));
+            result.push_str(&offset.block_only().to_string(context.config));
         } else {
             result.push(' ');
         }
diff --git a/src/lib.rs b/src/lib.rs
index abe9a6e8554..43054698b2d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -133,6 +133,13 @@ impl Indent {
         Indent::new(0, 0)
     }
 
+    pub fn block_only(&self) -> Indent {
+        Indent {
+            block_indent: self.block_indent,
+            alignment: 0,
+        }
+    }
+
     pub fn block_indent(mut self, config: &Config) -> Indent {
         self.block_indent += config.tab_spaces;
         self
@@ -204,7 +211,11 @@ impl Sub<usize> for Indent {
 #[derive(Copy, Clone, Debug)]
 pub struct Shape {
     pub width: usize,
+    // The current indentation of code.
     pub indent: Indent,
+    // Indentation + any already emitted text on the first line of the current
+    // statement.
+    pub offset: usize,
 }
 
 impl Shape {
@@ -212,6 +223,7 @@ impl Shape {
         Shape {
             width: config.max_width,
             indent: indent,
+            offset: indent.width(),
         }
     }
 
@@ -234,15 +246,93 @@ impl Shape {
         Shape {
             width: width,
             indent: indent,
+            offset: indent.alignment,
         }
     }
 
-    pub fn sub_width(self, width: usize) -> Shape {
+    pub fn offset(width: usize, indent: Indent, offset: usize) -> Shape {
         Shape {
-            width: self.width - width,
-            indent: self.indent,
+            width: width,
+            indent: indent,
+            offset: offset,
+        }
+    }    
+
+    pub fn visual_indent(&self, extra_width: usize) -> Shape {
+        let alignment = self.offset + extra_width;
+        Shape {
+            width: self.width,
+            indent: Indent {
+                block_indent: self.indent.block_indent,
+                alignment: alignment,
+            },
+            offset: alignment,
+        }
+    }
+
+    pub fn block_indent(&self, extra_width: usize) -> Shape {
+        if self.indent.alignment == 0 {
+            Shape {
+                width: self.width,
+                indent: Indent {
+                    block_indent: self.indent.block_indent + extra_width,
+                    alignment: 0,
+                },
+                offset: 0,
+            }
+        } else {
+            Shape {
+                width: self.width,
+                indent: Indent {
+                    block_indent: self.indent.block_indent,
+                    alignment: self.indent.alignment + extra_width,
+                },
+                offset: self.indent.alignment + extra_width,
+            }
         }
     }
+
+    pub fn add_offset(&self, extra_width: usize) -> Shape {
+        Shape {
+            width: self.width,
+            indent: Indent {
+                block_indent: self.indent.block_indent,
+                alignment: self.indent.alignment,
+            },
+            offset: self.offset + extra_width,
+        }
+    }
+
+    pub fn block(&self) -> Shape {
+        Shape {
+            width: self.width,
+            indent: Indent {
+                block_indent: self.indent.block_indent,
+                alignment: 0,
+            },
+            offset: self.offset,
+        }
+    }
+
+    pub fn sub_width(&self, width: usize) -> Option<Shape> {
+        Some(Shape {
+            width: try_opt!(self.width.checked_sub(width)),
+            indent: self.indent,
+            offset: self.offset,
+        })
+    }
+
+    pub fn shrink_left(&self, width: usize) -> Option<Shape> {
+        Some(Shape {
+            width: try_opt!(self.width.checked_sub(width)),
+            indent: self.indent + width,
+            offset: self.offset + width,
+        })        
+    }
+
+    pub fn used_width(&self) -> usize {
+        self.indent.block_indent + self.offset
+    }
 }
 
 pub enum ErrorKind {
diff --git a/src/macros.rs b/src/macros.rs
index 3a184d242ef..97c60adb684 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -155,15 +155,14 @@ pub fn rewrite_macro(mac: &ast::Mac,
         MacroStyle::Brackets => {
             // Format macro invocation as array literal.
             let extra_offset = macro_name.len();
+            let shape = try_opt!(shape.shrink_left(extra_offset));
             let rewrite = try_opt!(rewrite_array(expr_vec.iter().map(|x| &**x),
                                                  mk_sp(context.codemap
                                                            .span_after(mac.span,
                                                                        original_style.opener()),
                                                        mac.span.hi - BytePos(1)),
                                                  context,
-                                                 Shape::legacy(try_opt!(shape.width
-                                                                   .checked_sub(extra_offset)),
-                                                               shape.indent + extra_offset)));
+                                                 shape));
 
             Some(format!("{}{}", macro_name, rewrite))
         }
diff --git a/src/patterns.rs b/src/patterns.rs
index 7346dc08010..73b54a66626 100644
--- a/src/patterns.rs
+++ b/src/patterns.rs
@@ -220,16 +220,16 @@ 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!(shape.width.checked_sub(path_len + if add_comma { 3 } else { 2 }));
+        let nested_shape = try_opt!(shape.sub_width(path_len + if add_comma { 3 } else { 2 }));
         // 1 = "(".len()
-        let offset = shape.indent + path_len + 1;
+        let nested_shape = nested_shape.visual_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)),
+                         |item| item.rewrite(context, nested_shape),
                          context.codemap.span_after(span, "("),
                          span.hi - BytePos(1))
                 .collect();
@@ -242,10 +242,10 @@ 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, Shape::legacy(width, offset), context.config))
+            try_opt!(format_item_list(da_iter, nested_shape, context.config))
         } else {
             try_opt!(format_item_list(items.into_iter(),
-                                      Shape::legacy(width, offset),
+                                      nested_shape,
                                       context.config))
         };
 
diff --git a/src/rewrite.rs b/src/rewrite.rs
index 3c69e2c3c45..bb75a6f4db7 100644
--- a/src/rewrite.rs
+++ b/src/rewrite.rs
@@ -13,7 +13,7 @@
 use syntax::codemap::{CodeMap, Span};
 use syntax::parse::ParseSess;
 
-use {Indent, Shape};
+use Shape;
 use config::Config;
 
 pub trait Rewrite {
@@ -26,20 +26,9 @@ pub struct RewriteContext<'a> {
     pub parse_session: &'a ParseSess,
     pub codemap: &'a CodeMap,
     pub config: &'a Config,
-    // Indentation due to nesting of blocks.
-    pub block_indent: Indent,
 }
 
 impl<'a> RewriteContext<'a> {
-    pub fn nested_context(&self) -> RewriteContext<'a> {
-        RewriteContext {
-            parse_session: self.parse_session,
-            codemap: self.codemap,
-            config: self.config,
-            block_indent: self.block_indent.block_indent(self.config),
-        }
-    }
-
     pub fn snippet(&self, span: Span) -> String {
         self.codemap.span_to_snippet(span).unwrap()
     }
diff --git a/src/string.rs b/src/string.rs
index 014830a9155..7fd6bb78e61 100644
--- a/src/string.rs
+++ b/src/string.rs
@@ -36,7 +36,8 @@ 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.shape.indent.to_string(fmt.config);
+    let shape = fmt.shape.visual_indent(0);
+    let indent = shape.indent.to_string(fmt.config);
     let punctuation = ":,;.";
 
     // `cur_start` is the position in `orig` of the start of the current line.
@@ -49,7 +50,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.shape.width.checked_sub(fmt.opener.len() + ender_length + 1)) + 1;
+    let max_chars = try_opt!(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.
diff --git a/src/types.rs b/src/types.rs
index cce487cb2ea..d636f6e587a 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -66,9 +66,9 @@ pub fn rewrite_path(context: &RewriteContext,
                 result.push_str("::");
             }
 
-            let extra_offset = extra_offset(&result, shape.indent);
+            let extra_offset = extra_offset(&result, shape);
             // 3 = ">::".len()
-            let budget = try_opt!(shape.width.checked_sub(extra_offset + 3));
+            let shape = try_opt!(try_opt!(shape.shrink_left(extra_offset)).sub_width(3));
 
             result = try_opt!(rewrite_path_segments(PathContext::Type,
                                                     result,
@@ -76,8 +76,7 @@ pub fn rewrite_path(context: &RewriteContext,
                                                     span_lo,
                                                     path.span.hi,
                                                     context,
-                                                    Shape::legacy(budget,
-                                                                  shape.indent + extra_offset)));
+                                                    shape));
         }
 
         if context.config.spaces_within_angle_brackets {
@@ -88,15 +87,15 @@ pub fn rewrite_path(context: &RewriteContext,
         span_lo = qself.ty.span.hi + BytePos(1);
     }
 
-    let extra_offset = extra_offset(&result, shape.indent);
-    let budget = try_opt!(shape.width.checked_sub(extra_offset));
+    let extra_offset = extra_offset(&result, shape);
+    let shape = try_opt!(shape.shrink_left(extra_offset));
     rewrite_path_segments(path_context,
                           result,
                           path.segments.iter().skip(skip_count),
                           span_lo,
                           path.span.hi,
                           context,
-                          Shape::legacy(budget, shape.indent + extra_offset))
+                          shape)
 }
 
 fn rewrite_path_segments<'a, I>(path_context: PathContext,
@@ -110,6 +109,7 @@ fn rewrite_path_segments<'a, I>(path_context: PathContext,
     where I: Iterator<Item = &'a ast::PathSegment>
 {
     let mut first = true;
+    let shape = shape.visual_indent(0);
 
     for segment in iter {
         // Indicates a global path, shouldn't be rendered.
@@ -122,15 +122,14 @@ fn rewrite_path_segments<'a, I>(path_context: PathContext,
             buffer.push_str("::");
         }
 
-        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 extra_offset = extra_offset(&buffer, shape);
+        let new_shape = try_opt!(shape.shrink_left(extra_offset));
         let segment_string = try_opt!(rewrite_segment(path_context,
                                                       segment,
                                                       &mut span_lo,
                                                       span_hi,
                                                       context,
-                                                      Shape::legacy(remaining_width, new_offset)));
+                                                      new_shape));
 
         buffer.push_str(&segment_string);
     }
@@ -190,8 +189,7 @@ fn rewrite_segment(path_context: PathContext,
                    shape: Shape)
                    -> Option<String> {
     let ident_len = segment.identifier.to_string().len();
-    let width = try_opt!(shape.width.checked_sub(ident_len));
-    let offset = shape.indent + ident_len;
+    let shape = try_opt!(shape.shrink_left(ident_len));
 
     let params = if let Some(ref params) = segment.parameters {
         match **params {
@@ -216,24 +214,18 @@ fn rewrite_segment(path_context: PathContext,
                 // 1 for <
                 let extra_offset = 1 + separator.len();
                 // 1 for >
-                let list_width = try_opt!(width.checked_sub(extra_offset + 1));
+                // TODO bad visual indent
+                let list_shape = try_opt!(try_opt!(shape.shrink_left(extra_offset)).sub_width(1)).visual_indent(0);
 
                 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))
-                                         },
+                                         |seg| seg.rewrite(context, list_shape),
                                          list_lo,
                                          span_hi);
-                let list_str = try_opt!(format_item_list(items,
-                                                         Shape::legacy(list_width,
-                                                                       offset + extra_offset),
-                                                         context.config));
+                let list_str = try_opt!(format_item_list(items, list_shape, context.config));
 
                 // Update position of last bracket.
                 *span_lo = next_span_lo;
@@ -254,7 +246,7 @@ fn rewrite_segment(path_context: PathContext,
                                               false,
                                               data.span,
                                               context,
-                                              Shape::legacy(width, offset)))
+                                              shape))
             }
             _ => String::new(),
         }
diff --git a/src/utils.rs b/src/utils.rs
index c9017571025..da48831e672 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -18,16 +18,16 @@ use syntax::ast::{self, Visibility, Attribute, MetaItem, MetaItemKind, NestedMet
 use syntax::codemap::BytePos;
 use syntax::abi;
 
-use {Indent, Shape};
+use Shape;
 use rewrite::{Rewrite, RewriteContext};
 
 use SKIP_ANNOTATION;
 
 // Computes the length of a string's last line, minus offset.
-pub fn extra_offset(text: &str, offset: Indent) -> usize {
+pub fn extra_offset(text: &str, shape: Shape) -> usize {
     match text.rfind('\n') {
         // 1 for newline character
-        Some(idx) => text.len().checked_sub(idx + 1 + offset.width()).unwrap_or(0),
+        Some(idx) => text.len().checked_sub(idx + 1 + shape.used_width()).unwrap_or(0),
         None => text.len(),
     }
 }
diff --git a/src/visitor.rs b/src/visitor.rs
index fd0a1f1ed21..e7fd3beda2f 100644
--- a/src/visitor.rs
+++ b/src/visitor.rs
@@ -294,6 +294,7 @@ impl<'a> FmtVisitor<'a> {
                                              ty,
                                              mutability,
                                              Some(expr),
+                                             self.block_indent,
                                              &self.get_context());
                 self.push_rewrite(item.span, rewrite);
             }
@@ -304,6 +305,7 @@ impl<'a> FmtVisitor<'a> {
                                              ty,
                                              ast::Mutability::Immutable,
                                              Some(expr),
+                                             self.block_indent,
                                              &self.get_context());
                 self.push_rewrite(item.span, rewrite);
             }
@@ -353,6 +355,7 @@ impl<'a> FmtVisitor<'a> {
                                              ty,
                                              ast::Mutability::Immutable,
                                              expr_opt.as_ref(),
+                                             self.block_indent,
                                              &self.get_context());
                 self.push_rewrite(ti.span, rewrite);
             }
@@ -403,6 +406,7 @@ impl<'a> FmtVisitor<'a> {
                                              ty,
                                              ast::Mutability::Immutable,
                                              Some(expr),
+                                             self.block_indent,
                                              &self.get_context());
                 self.push_rewrite(ii.span, rewrite);
             }
@@ -560,7 +564,6 @@ impl<'a> FmtVisitor<'a> {
             parse_session: self.parse_session,
             codemap: self.codemap,
             config: self.config,
-            block_indent: self.block_indent,
         }
     }
 }