about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSeiichi Uchida <seuchida@gmail.com>2017-09-17 15:23:25 +0900
committertopecongiro <seuchida@gmail.com>2017-09-19 10:04:35 +0900
commitdf7d2be5629b7162426f8a3c6dbf005f228008fe (patch)
treecdb7353ef2d0deaeee4919b182bcca6796176e4e
parent8974f89381cffd73c35b0cb91eb4e49a66f93d5c (diff)
Move Indent and Shape to shape.rs from lib.rs
-rw-r--r--src/chains.rs2
-rw-r--r--src/comment.rs4
-rw-r--r--src/expr.rs3
-rw-r--r--src/imports.rs3
-rw-r--r--src/items.rs3
-rw-r--r--src/lib.rs341
-rw-r--r--src/lists.rs2
-rw-r--r--src/macros.rs2
-rw-r--r--src/missed_spans.rs5
-rw-r--r--src/patterns.rs3
-rw-r--r--src/rewrite.rs2
-rw-r--r--src/shape.rs344
-rw-r--r--src/string.rs5
-rw-r--r--src/types.rs3
-rw-r--r--src/utils.rs2
-rw-r--r--src/vertical.rs3
-rw-r--r--src/visitor.rs3
17 files changed, 372 insertions, 358 deletions
diff --git a/src/chains.rs b/src/chains.rs
index f6055927f86..23c311b9589 100644
--- a/src/chains.rs
+++ b/src/chains.rs
@@ -76,7 +76,7 @@
 ///     .qux
 /// ```
 
-use Shape;
+use shape::Shape;
 use config::IndentStyle;
 use expr::rewrite_call;
 use macros::convert_try_mac;
diff --git a/src/comment.rs b/src/comment.rs
index 953060be226..f74684e5862 100644
--- a/src/comment.rs
+++ b/src/comment.rs
@@ -14,9 +14,9 @@ use std::{self, iter};
 
 use syntax::codemap::Span;
 
-use {Indent, Shape};
 use config::Config;
 use rewrite::RewriteContext;
+use shape::{Indent, Shape};
 use string::{rewrite_string, StringFormat};
 use utils::{first_line_width, last_line_width};
 
@@ -928,7 +928,7 @@ fn remove_comment_header(comment: &str) -> &str {
 mod test {
     use super::{contains_comment, rewrite_comment, CharClasses, CodeCharKind, CommentCodeSlices,
                 FindUncommented, FullCodeCharKind};
-    use {Indent, Shape};
+    use shape::{Indent, Shape};
 
     #[test]
     fn char_classes() {
diff --git a/src/expr.rs b/src/expr.rs
index e1cf24cdf2b..91e63d256bd 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -17,7 +17,7 @@ use syntax::{ast, ptr};
 use syntax::codemap::{BytePos, CodeMap, Span};
 use syntax::parse::classify;
 
-use {Indent, Shape, Spanned};
+use Spanned;
 use chains::rewrite_chain;
 use codemap::{LineRangeUtils, SpanUtils};
 use comment::{combine_strs_with_missing_comments, contains_comment, recover_comment_removed,
@@ -30,6 +30,7 @@ use lists::{definitive_tactic, itemize_list, shape_for_tactic, struct_lit_format
 use macros::{rewrite_macro, MacroArg, MacroPosition};
 use patterns::{can_be_overflowed_pat, TuplePatField};
 use rewrite::{Rewrite, RewriteContext};
+use shape::{Indent, Shape};
 use string::{rewrite_string, StringFormat};
 use types::{can_be_overflowed_type, rewrite_path, PathContext};
 use utils::{colon_spaces, contains_skip, extra_offset, first_line_width, inner_attributes,
diff --git a/src/imports.rs b/src/imports.rs
index 98a57fc112f..aa778341f2a 100644
--- a/src/imports.rs
+++ b/src/imports.rs
@@ -13,13 +13,14 @@ use std::cmp::Ordering;
 use syntax::ast;
 use syntax::codemap::{BytePos, Span};
 
-use {Shape, Spanned};
+use Spanned;
 use codemap::SpanUtils;
 use comment::combine_strs_with_missing_comments;
 use config::IndentStyle;
 use lists::{definitive_tactic, itemize_list, write_list, DefinitiveListTactic, ListFormatting,
             ListItem, Separator, SeparatorPlace, SeparatorTactic};
 use rewrite::{Rewrite, RewriteContext};
+use shape::Shape;
 use types::{rewrite_path, PathContext};
 use utils::{format_visibility, mk_sp};
 use visitor::{rewrite_extern_crate, FmtVisitor};
diff --git a/src/items.rs b/src/items.rs
index 13c669d698f..4fcd0d8b494 100644
--- a/src/items.rs
+++ b/src/items.rs
@@ -16,7 +16,7 @@ use syntax::{abi, ast, ptr, symbol};
 use syntax::ast::ImplItem;
 use syntax::codemap::{BytePos, Span};
 
-use {Indent, Shape, Spanned};
+use Spanned;
 use codemap::{LineRangeUtils, SpanUtils};
 use comment::{combine_strs_with_missing_comments, contains_comment, recover_comment_removed,
               recover_missing_comment_in_span, rewrite_missing_comment, FindUncommented};
@@ -26,6 +26,7 @@ use expr::{format_expr, is_empty_block, is_simple_block_stmt, rewrite_assign_rhs
 use lists::{definitive_tactic, itemize_list, write_list, DefinitiveListTactic, ListFormatting,
             ListItem, ListTactic, Separator, SeparatorPlace, SeparatorTactic};
 use rewrite::{Rewrite, RewriteContext};
+use shape::{Indent, Shape};
 use types::join_bounds;
 use utils::{colon_spaces, contains_skip, end_typaram, first_line_width, format_abi,
             format_constness, format_defaultness, format_mutability, format_unsafety,
diff --git a/src/lib.rs b/src/lib.rs
index 043f6f506d0..2e95966694f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -29,7 +29,6 @@ use std::collections::HashMap;
 use std::fmt;
 use std::io::{self, stdout, Write};
 use std::iter::repeat;
-use std::ops::{Add, Sub};
 use std::path::{Path, PathBuf};
 use std::rc::Rc;
 
@@ -52,6 +51,7 @@ pub use self::summary::Summary;
 
 #[macro_use]
 mod utils;
+mod shape;
 pub mod config;
 pub mod codemap;
 pub mod filemap;
@@ -239,260 +239,6 @@ impl Spanned for MacroArg {
     }
 }
 
-#[derive(Copy, Clone, Debug)]
-pub struct Indent {
-    // Width of the block indent, in characters. Must be a multiple of
-    // Config::tab_spaces.
-    pub block_indent: usize,
-    // Alignment in characters.
-    pub alignment: usize,
-}
-
-// INDENT_BUFFER.len() == 60
-const INDENT_BUFFER: &str = "                                                            ";
-const INDENT_BUFFER_LEN: usize = 60;
-
-impl Indent {
-    pub fn new(block_indent: usize, alignment: usize) -> Indent {
-        Indent {
-            block_indent: block_indent,
-            alignment: alignment,
-        }
-    }
-
-    pub fn from_width(config: &Config, width: usize) -> Indent {
-        if config.hard_tabs() {
-            let tab_num = width / config.tab_spaces();
-            let alignment = width % config.tab_spaces();
-            Indent::new(config.tab_spaces() * tab_num, alignment)
-        } else {
-            Indent::new(width, 0)
-        }
-    }
-
-    pub fn empty() -> 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
-    }
-
-    pub fn block_unindent(mut self, config: &Config) -> Indent {
-        if self.block_indent < config.tab_spaces() {
-            Indent::new(self.block_indent, 0)
-        } else {
-            self.block_indent -= config.tab_spaces();
-            self
-        }
-    }
-
-    pub fn width(&self) -> usize {
-        self.block_indent + self.alignment
-    }
-
-    pub fn to_string(&self, config: &Config) -> Cow<'static, str> {
-        let (num_tabs, num_spaces) = if config.hard_tabs() {
-            (self.block_indent / config.tab_spaces(), self.alignment)
-        } else {
-            (0, self.width())
-        };
-        let num_chars = num_tabs + num_spaces;
-        if num_tabs == 0 && num_chars <= INDENT_BUFFER_LEN {
-            Cow::from(&INDENT_BUFFER[..num_chars])
-        } else {
-            let mut indent = String::with_capacity(num_chars);
-            for _ in 0..num_tabs {
-                indent.push('\t')
-            }
-            for _ in 0..num_spaces {
-                indent.push(' ')
-            }
-            Cow::from(indent)
-        }
-    }
-}
-
-impl Add for Indent {
-    type Output = Indent;
-
-    fn add(self, rhs: Indent) -> Indent {
-        Indent {
-            block_indent: self.block_indent + rhs.block_indent,
-            alignment: self.alignment + rhs.alignment,
-        }
-    }
-}
-
-impl Sub for Indent {
-    type Output = Indent;
-
-    fn sub(self, rhs: Indent) -> Indent {
-        Indent::new(
-            self.block_indent - rhs.block_indent,
-            self.alignment - rhs.alignment,
-        )
-    }
-}
-
-impl Add<usize> for Indent {
-    type Output = Indent;
-
-    fn add(self, rhs: usize) -> Indent {
-        Indent::new(self.block_indent, self.alignment + rhs)
-    }
-}
-
-impl Sub<usize> for Indent {
-    type Output = Indent;
-
-    fn sub(self, rhs: usize) -> Indent {
-        Indent::new(self.block_indent, self.alignment - rhs)
-    }
-}
-
-#[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 {
-    /// `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,
-            offset: indent.alignment,
-        }
-    }
-
-    pub fn indented(indent: Indent, config: &Config) -> Shape {
-        Shape {
-            width: config.max_width().checked_sub(indent.width()).unwrap_or(0),
-            indent: indent,
-            offset: indent.alignment,
-        }
-    }
-
-    pub fn with_max_width(&self, config: &Config) -> Shape {
-        Shape {
-            width: config
-                .max_width()
-                .checked_sub(self.indent.width())
-                .unwrap_or(0),
-            ..*self
-        }
-    }
-
-    pub fn offset(width: usize, indent: Indent, offset: usize) -> Shape {
-        Shape {
-            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::new(self.indent.block_indent, alignment),
-            offset: alignment,
-        }
-    }
-
-    pub fn block_indent(&self, extra_width: usize) -> Shape {
-        if self.indent.alignment == 0 {
-            Shape {
-                width: self.width,
-                indent: Indent::new(self.indent.block_indent + extra_width, 0),
-                offset: 0,
-            }
-        } else {
-            Shape {
-                width: self.width,
-                indent: self.indent + extra_width,
-                offset: self.indent.alignment + extra_width,
-            }
-        }
-    }
-
-    pub fn block_left(&self, width: usize) -> Option<Shape> {
-        self.block_indent(width).sub_width(width)
-    }
-
-    pub fn add_offset(&self, extra_width: usize) -> Shape {
-        Shape {
-            offset: self.offset + extra_width,
-            ..*self
-        }
-    }
-
-    pub fn block(&self) -> Shape {
-        Shape {
-            indent: self.indent.block_only(),
-            ..*self
-        }
-    }
-
-    pub fn sub_width(&self, width: usize) -> Option<Shape> {
-        Some(Shape {
-            width: try_opt!(self.width.checked_sub(width)),
-            ..*self
-        })
-    }
-
-    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 offset_left(&self, width: usize) -> Option<Shape> {
-        self.add_offset(width).sub_width(width)
-    }
-
-    pub fn used_width(&self) -> usize {
-        self.indent.block_indent + self.offset
-    }
-
-    pub fn rhs_overhead(&self, config: &Config) -> usize {
-        config
-            .max_width()
-            .checked_sub(self.used_width() + self.width)
-            .unwrap_or(0)
-    }
-}
-
 pub enum ErrorKind {
     // Line has exceeded character limit (found, maximum)
     LineOverflow(usize, usize),
@@ -1000,88 +746,3 @@ pub fn run(input: Input, config: &Config) -> Summary {
         }
     }
 }
-
-#[cfg(test)]
-mod test {
-    use super::*;
-
-    #[test]
-    fn indent_add_sub() {
-        let indent = Indent::new(4, 8) + Indent::new(8, 12);
-        assert_eq!(12, indent.block_indent);
-        assert_eq!(20, indent.alignment);
-
-        let indent = indent - Indent::new(4, 4);
-        assert_eq!(8, indent.block_indent);
-        assert_eq!(16, indent.alignment);
-    }
-
-    #[test]
-    fn indent_add_sub_alignment() {
-        let indent = Indent::new(4, 8) + 4;
-        assert_eq!(4, indent.block_indent);
-        assert_eq!(12, indent.alignment);
-
-        let indent = indent - 4;
-        assert_eq!(4, indent.block_indent);
-        assert_eq!(8, indent.alignment);
-    }
-
-    #[test]
-    fn indent_to_string_spaces() {
-        let config = Config::default();
-        let indent = Indent::new(4, 8);
-
-        // 12 spaces
-        assert_eq!("            ", indent.to_string(&config));
-    }
-
-    #[test]
-    fn indent_to_string_hard_tabs() {
-        let mut config = Config::default();
-        config.set().hard_tabs(true);
-        let indent = Indent::new(8, 4);
-
-        // 2 tabs + 4 spaces
-        assert_eq!("\t\t    ", indent.to_string(&config));
-    }
-
-    #[test]
-    fn shape_visual_indent() {
-        let config = Config::default();
-        let indent = Indent::new(4, 8);
-        let shape = Shape::legacy(config.max_width(), indent);
-        let shape = shape.visual_indent(20);
-
-        assert_eq!(config.max_width(), shape.width);
-        assert_eq!(4, shape.indent.block_indent);
-        assert_eq!(28, shape.indent.alignment);
-        assert_eq!(28, shape.offset);
-    }
-
-    #[test]
-    fn shape_block_indent_without_alignment() {
-        let config = Config::default();
-        let indent = Indent::new(4, 0);
-        let shape = Shape::legacy(config.max_width(), indent);
-        let shape = shape.block_indent(20);
-
-        assert_eq!(config.max_width(), shape.width);
-        assert_eq!(24, shape.indent.block_indent);
-        assert_eq!(0, shape.indent.alignment);
-        assert_eq!(0, shape.offset);
-    }
-
-    #[test]
-    fn shape_block_indent_with_alignment() {
-        let config = Config::default();
-        let indent = Indent::new(4, 8);
-        let shape = Shape::legacy(config.max_width(), indent);
-        let shape = shape.block_indent(20);
-
-        assert_eq!(config.max_width(), shape.width);
-        assert_eq!(4, shape.indent.block_indent);
-        assert_eq!(28, shape.indent.alignment);
-        assert_eq!(28, shape.offset);
-    }
-}
diff --git a/src/lists.rs b/src/lists.rs
index 8fd21c7f737..f42e3ffe977 100644
--- a/src/lists.rs
+++ b/src/lists.rs
@@ -13,10 +13,10 @@ use std::iter::Peekable;
 
 use syntax::codemap::{BytePos, CodeMap};
 
-use {Indent, Shape};
 use comment::{find_comment_end, rewrite_comment, FindUncommented};
 use config::{Config, IndentStyle};
 use rewrite::RewriteContext;
+use shape::{Indent, Shape};
 use utils::{first_line_width, last_line_width, mk_sp};
 
 /// Formatting tactic for lists. This will be cast down to a
diff --git a/src/macros.rs b/src/macros.rs
index a1254b4541d..5b52567087e 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -28,11 +28,11 @@ use syntax::symbol;
 use syntax::tokenstream::TokenStream;
 use syntax::util::ThinVec;
 
-use {Indent, Shape};
 use codemap::SpanUtils;
 use comment::{contains_comment, FindUncommented};
 use expr::{rewrite_array, rewrite_call_inner};
 use rewrite::{Rewrite, RewriteContext};
+use shape::{Indent, Shape};
 use utils::mk_sp;
 
 const FORCED_BRACKET_MACROS: &'static [&'static str] = &["vec!"];
diff --git a/src/missed_spans.rs b/src/missed_spans.rs
index 32e321dd257..9d20e6c6e55 100644
--- a/src/missed_spans.rs
+++ b/src/missed_spans.rs
@@ -10,10 +10,11 @@
 
 use std::borrow::Cow;
 
-use {Indent, Shape};
+use syntax::codemap::{BytePos, Pos, Span};
+
 use comment::{rewrite_comment, CodeCharKind, CommentCodeSlices};
 use config::WriteMode;
-use syntax::codemap::{BytePos, Pos, Span};
+use shape::{Indent, Shape};
 use utils::mk_sp;
 use visitor::FmtVisitor;
 
diff --git a/src/patterns.rs b/src/patterns.rs
index ab61df22b68..49c7556894f 100644
--- a/src/patterns.rs
+++ b/src/patterns.rs
@@ -12,7 +12,7 @@ use syntax::ast::{self, BindingMode, FieldPat, Pat, PatKind, RangeEnd};
 use syntax::codemap::{self, BytePos, Span};
 use syntax::ptr;
 
-use {Shape, Spanned};
+use Spanned;
 use codemap::SpanUtils;
 use comment::FindUncommented;
 use expr::{can_be_overflowed_expr, rewrite_call_inner, rewrite_pair, rewrite_unary_prefix,
@@ -20,6 +20,7 @@ use expr::{can_be_overflowed_expr, rewrite_call_inner, rewrite_pair, rewrite_una
 use lists::{itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape,
             struct_lit_tactic, write_list, DefinitiveListTactic, SeparatorPlace, SeparatorTactic};
 use rewrite::{Rewrite, RewriteContext};
+use shape::Shape;
 use types::{rewrite_path, PathContext};
 use utils::{format_mutability, mk_sp, wrap_str};
 
diff --git a/src/rewrite.rs b/src/rewrite.rs
index 6a7167537c9..e2be8ef086c 100644
--- a/src/rewrite.rs
+++ b/src/rewrite.rs
@@ -13,8 +13,8 @@
 use syntax::codemap::{CodeMap, Span};
 use syntax::parse::ParseSess;
 
-use Shape;
 use config::{Config, IndentStyle};
+use shape::Shape;
 
 pub trait Rewrite {
     /// Rewrite self into shape.
diff --git a/src/shape.rs b/src/shape.rs
new file mode 100644
index 00000000000..63910c29707
--- /dev/null
+++ b/src/shape.rs
@@ -0,0 +1,344 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::ops::{Add, Sub};
+
+use Config;
+
+#[derive(Copy, Clone, Debug)]
+pub struct Indent {
+    // Width of the block indent, in characters. Must be a multiple of
+    // Config::tab_spaces.
+    pub block_indent: usize,
+    // Alignment in characters.
+    pub alignment: usize,
+}
+
+impl Indent {
+    pub fn new(block_indent: usize, alignment: usize) -> Indent {
+        Indent {
+            block_indent: block_indent,
+            alignment: alignment,
+        }
+    }
+
+    pub fn from_width(config: &Config, width: usize) -> Indent {
+        if config.hard_tabs() {
+            let tab_num = width / config.tab_spaces();
+            let alignment = width % config.tab_spaces();
+            Indent::new(config.tab_spaces() * tab_num, alignment)
+        } else {
+            Indent::new(width, 0)
+        }
+    }
+
+    pub fn empty() -> 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
+    }
+
+    pub fn block_unindent(mut self, config: &Config) -> Indent {
+        if self.block_indent < config.tab_spaces() {
+            Indent::new(self.block_indent, 0)
+        } else {
+            self.block_indent -= config.tab_spaces();
+            self
+        }
+    }
+
+    pub fn width(&self) -> usize {
+        self.block_indent + self.alignment
+    }
+
+    pub fn to_string(&self, config: &Config) -> String {
+        let (num_tabs, num_spaces) = if config.hard_tabs() {
+            (self.block_indent / config.tab_spaces(), self.alignment)
+        } else {
+            (0, self.width())
+        };
+        let num_chars = num_tabs + num_spaces;
+        let mut indent = String::with_capacity(num_chars);
+        for _ in 0..num_tabs {
+            indent.push('\t')
+        }
+        for _ in 0..num_spaces {
+            indent.push(' ')
+        }
+        indent
+    }
+}
+
+impl Add for Indent {
+    type Output = Indent;
+
+    fn add(self, rhs: Indent) -> Indent {
+        Indent {
+            block_indent: self.block_indent + rhs.block_indent,
+            alignment: self.alignment + rhs.alignment,
+        }
+    }
+}
+
+impl Sub for Indent {
+    type Output = Indent;
+
+    fn sub(self, rhs: Indent) -> Indent {
+        Indent::new(
+            self.block_indent - rhs.block_indent,
+            self.alignment - rhs.alignment,
+        )
+    }
+}
+
+impl Add<usize> for Indent {
+    type Output = Indent;
+
+    fn add(self, rhs: usize) -> Indent {
+        Indent::new(self.block_indent, self.alignment + rhs)
+    }
+}
+
+impl Sub<usize> for Indent {
+    type Output = Indent;
+
+    fn sub(self, rhs: usize) -> Indent {
+        Indent::new(self.block_indent, self.alignment - rhs)
+    }
+}
+
+#[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 {
+    /// `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,
+            offset: indent.alignment,
+        }
+    }
+
+    pub fn indented(indent: Indent, config: &Config) -> Shape {
+        Shape {
+            width: config.max_width().checked_sub(indent.width()).unwrap_or(0),
+            indent: indent,
+            offset: indent.alignment,
+        }
+    }
+
+    pub fn with_max_width(&self, config: &Config) -> Shape {
+        Shape {
+            width: config
+                .max_width()
+                .checked_sub(self.indent.width())
+                .unwrap_or(0),
+            ..*self
+        }
+    }
+
+    pub fn offset(width: usize, indent: Indent, offset: usize) -> Shape {
+        Shape {
+            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::new(self.indent.block_indent, alignment),
+            offset: alignment,
+        }
+    }
+
+    pub fn block_indent(&self, extra_width: usize) -> Shape {
+        if self.indent.alignment == 0 {
+            Shape {
+                width: self.width,
+                indent: Indent::new(self.indent.block_indent + extra_width, 0),
+                offset: 0,
+            }
+        } else {
+            Shape {
+                width: self.width,
+                indent: self.indent + extra_width,
+                offset: self.indent.alignment + extra_width,
+            }
+        }
+    }
+
+    pub fn block_left(&self, width: usize) -> Option<Shape> {
+        self.block_indent(width).sub_width(width)
+    }
+
+    pub fn add_offset(&self, extra_width: usize) -> Shape {
+        Shape {
+            offset: self.offset + extra_width,
+            ..*self
+        }
+    }
+
+    pub fn block(&self) -> Shape {
+        Shape {
+            indent: self.indent.block_only(),
+            ..*self
+        }
+    }
+
+    pub fn sub_width(&self, width: usize) -> Option<Shape> {
+        Some(Shape {
+            width: try_opt!(self.width.checked_sub(width)),
+            ..*self
+        })
+    }
+
+    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 offset_left(&self, width: usize) -> Option<Shape> {
+        self.add_offset(width).sub_width(width)
+    }
+
+    pub fn used_width(&self) -> usize {
+        self.indent.block_indent + self.offset
+    }
+
+    pub fn rhs_overhead(&self, config: &Config) -> usize {
+        config
+            .max_width()
+            .checked_sub(self.used_width() + self.width)
+            .unwrap_or(0)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn indent_add_sub() {
+        let indent = Indent::new(4, 8) + Indent::new(8, 12);
+        assert_eq!(12, indent.block_indent);
+        assert_eq!(20, indent.alignment);
+
+        let indent = indent - Indent::new(4, 4);
+        assert_eq!(8, indent.block_indent);
+        assert_eq!(16, indent.alignment);
+    }
+
+    #[test]
+    fn indent_add_sub_alignment() {
+        let indent = Indent::new(4, 8) + 4;
+        assert_eq!(4, indent.block_indent);
+        assert_eq!(12, indent.alignment);
+
+        let indent = indent - 4;
+        assert_eq!(4, indent.block_indent);
+        assert_eq!(8, indent.alignment);
+    }
+
+    #[test]
+    fn indent_to_string_spaces() {
+        let config = Config::default();
+        let indent = Indent::new(4, 8);
+
+        // 12 spaces
+        assert_eq!("            ", indent.to_string(&config));
+    }
+
+    #[test]
+    fn indent_to_string_hard_tabs() {
+        let mut config = Config::default();
+        config.set().hard_tabs(true);
+        let indent = Indent::new(8, 4);
+
+        // 2 tabs + 4 spaces
+        assert_eq!("\t\t    ", indent.to_string(&config));
+    }
+
+    #[test]
+    fn shape_visual_indent() {
+        let config = Config::default();
+        let indent = Indent::new(4, 8);
+        let shape = Shape::legacy(config.max_width(), indent);
+        let shape = shape.visual_indent(20);
+
+        assert_eq!(config.max_width(), shape.width);
+        assert_eq!(4, shape.indent.block_indent);
+        assert_eq!(28, shape.indent.alignment);
+        assert_eq!(28, shape.offset);
+    }
+
+    #[test]
+    fn shape_block_indent_without_alignment() {
+        let config = Config::default();
+        let indent = Indent::new(4, 0);
+        let shape = Shape::legacy(config.max_width(), indent);
+        let shape = shape.block_indent(20);
+
+        assert_eq!(config.max_width(), shape.width);
+        assert_eq!(24, shape.indent.block_indent);
+        assert_eq!(0, shape.indent.alignment);
+        assert_eq!(0, shape.offset);
+    }
+
+    #[test]
+    fn shape_block_indent_with_alignment() {
+        let config = Config::default();
+        let indent = Indent::new(4, 8);
+        let shape = Shape::legacy(config.max_width(), indent);
+        let shape = shape.block_indent(20);
+
+        assert_eq!(config.max_width(), shape.width);
+        assert_eq!(4, shape.indent.block_indent);
+        assert_eq!(28, shape.indent.alignment);
+        assert_eq!(28, shape.offset);
+    }
+}
diff --git a/src/string.rs b/src/string.rs
index 84271f20082..8090926522d 100644
--- a/src/string.rs
+++ b/src/string.rs
@@ -13,8 +13,8 @@
 use regex::Regex;
 use unicode_segmentation::UnicodeSegmentation;
 
-use Shape;
 use config::Config;
+use shape::Shape;
 use utils::wrap_str;
 
 const MIN_STRING: usize = 10;
@@ -128,6 +128,7 @@ pub fn rewrite_string<'a>(orig: &str, fmt: &StringFormat<'a>) -> Option<String>
 #[cfg(test)]
 mod test {
     use super::{rewrite_string, StringFormat};
+    use shape::{Indent, Shape};
 
     #[test]
     fn issue343() {
@@ -137,7 +138,7 @@ mod test {
             closer: "\"",
             line_start: " ",
             line_end: "\\",
-            shape: ::Shape::legacy(2, ::Indent::empty()),
+            shape: Shape::legacy(2, Indent::empty()),
             trim_end: false,
             config: &config,
         };
diff --git a/src/types.rs b/src/types.rs
index 65bcf872bf5..e6cf052d142 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -17,7 +17,7 @@ use syntax::codemap::{self, BytePos, Span};
 use syntax::print::pprust;
 use syntax::symbol::keywords;
 
-use {Shape, Spanned};
+use Spanned;
 use codemap::SpanUtils;
 use config::{IndentStyle, Style, TypeDensity};
 use expr::{rewrite_pair, rewrite_tuple, rewrite_unary_prefix, wrap_args_with_parens};
@@ -25,6 +25,7 @@ use items::{format_generics_item_list, generics_shape_from_config};
 use lists::{definitive_tactic, itemize_list, write_list, ListFormatting, ListTactic, Separator,
             SeparatorPlace, SeparatorTactic};
 use rewrite::{Rewrite, RewriteContext};
+use shape::Shape;
 use utils::{colon_spaces, extra_offset, format_mutability, last_line_width, mk_sp, wrap_str};
 
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
diff --git a/src/utils.rs b/src/utils.rs
index 3a53d6c48fc..3c97044f5c4 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -15,8 +15,8 @@ use syntax::ast::{self, Attribute, MetaItem, MetaItemKind, NestedMetaItem, Neste
                   Path, Visibility};
 use syntax::codemap::{BytePos, Span, NO_EXPANSION};
 
-use Shape;
 use rewrite::{Rewrite, RewriteContext};
+use shape::Shape;
 
 // When we get scoped annotations, we should have rustfmt::skip.
 const SKIP_ANNOTATION: &'static str = "rustfmt_skip";
diff --git a/src/vertical.rs b/src/vertical.rs
index a120eb868e5..13de2e22e5d 100644
--- a/src/vertical.rs
+++ b/src/vertical.rs
@@ -15,7 +15,7 @@ use std::cmp;
 use syntax::ast;
 use syntax::codemap::{BytePos, Span};
 
-use {Indent, Shape, Spanned};
+use Spanned;
 use codemap::SpanUtils;
 use comment::{combine_strs_with_missing_comments, contains_comment};
 use expr::rewrite_field;
@@ -23,6 +23,7 @@ use items::{rewrite_struct_field, rewrite_struct_field_prefix};
 use lists::{definitive_tactic, itemize_list, write_list, ListFormatting, ListTactic, Separator,
             SeparatorPlace};
 use rewrite::{Rewrite, RewriteContext};
+use shape::{Indent, Shape};
 use utils::{contains_skip, is_attributes_extendable, mk_sp};
 
 pub trait AlignedItem {
diff --git a/src/visitor.rs b/src/visitor.rs
index 32ab6af282e..fa9c8e3b537 100644
--- a/src/visitor.rs
+++ b/src/visitor.rs
@@ -16,7 +16,7 @@ use syntax::attr::HasAttrs;
 use syntax::codemap::{self, BytePos, CodeMap, Pos, Span};
 use syntax::parse::ParseSess;
 
-use {Indent, Shape, Spanned};
+use Spanned;
 use codemap::{LineRangeUtils, SpanUtils};
 use comment::{contains_comment, recover_missing_comment_in_span, CodeCharKind, CommentCodeSlices,
               FindUncommented};
@@ -30,6 +30,7 @@ use lists::{itemize_list, write_list, DefinitiveListTactic, ListFormatting, Sepa
 use macros::{rewrite_macro, MacroPosition};
 use regex::Regex;
 use rewrite::{Rewrite, RewriteContext};
+use shape::{Indent, Shape};
 use utils::{self, contains_skip, inner_attributes, mk_sp, ptr_vec_to_ref_vec};
 
 fn is_use_item(item: &ast::Item) -> bool {