diff options
| author | Seiichi Uchida <seuchida@gmail.com> | 2017-09-17 15:23:25 +0900 |
|---|---|---|
| committer | topecongiro <seuchida@gmail.com> | 2017-09-19 10:04:35 +0900 |
| commit | df7d2be5629b7162426f8a3c6dbf005f228008fe (patch) | |
| tree | cdb7353ef2d0deaeee4919b182bcca6796176e4e /src/shape.rs | |
| parent | 8974f89381cffd73c35b0cb91eb4e49a66f93d5c (diff) | |
| download | rust-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.rs | 344 |
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); + } +} |
