about summary refs log tree commit diff
path: root/src/shape.rs
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 /src/shape.rs
parent8974f89381cffd73c35b0cb91eb4e49a66f93d5c (diff)
downloadrust-df7d2be5629b7162426f8a3c6dbf005f228008fe.tar.gz
rust-df7d2be5629b7162426f8a3c6dbf005f228008fe.zip
Move Indent and Shape to shape.rs from lib.rs
Diffstat (limited to 'src/shape.rs')
-rw-r--r--src/shape.rs344
1 files changed, 344 insertions, 0 deletions
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);
+    }
+}