1 // Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use std::ops::{Add, Sub};
15 #[derive(Copy, Clone, Debug)]
17 // Width of the block indent, in characters. Must be a multiple of
18 // Config::tab_spaces.
19 pub block_indent: usize,
20 // Alignment in characters.
25 pub fn new(block_indent: usize, alignment: usize) -> Indent {
27 block_indent: block_indent,
32 pub fn from_width(config: &Config, width: usize) -> Indent {
33 if config.hard_tabs() {
34 let tab_num = width / config.tab_spaces();
35 let alignment = width % config.tab_spaces();
36 Indent::new(config.tab_spaces() * tab_num, alignment)
42 pub fn empty() -> Indent {
46 pub fn block_only(&self) -> Indent {
48 block_indent: self.block_indent,
53 pub fn block_indent(mut self, config: &Config) -> Indent {
54 self.block_indent += config.tab_spaces();
58 pub fn block_unindent(mut self, config: &Config) -> Indent {
59 if self.block_indent < config.tab_spaces() {
60 Indent::new(self.block_indent, 0)
62 self.block_indent -= config.tab_spaces();
67 pub fn width(&self) -> usize {
68 self.block_indent + self.alignment
71 pub fn to_string(&self, config: &Config) -> String {
72 let (num_tabs, num_spaces) = if config.hard_tabs() {
73 (self.block_indent / config.tab_spaces(), self.alignment)
77 let num_chars = num_tabs + num_spaces;
78 let mut indent = String::with_capacity(num_chars);
79 for _ in 0..num_tabs {
82 for _ in 0..num_spaces {
92 fn add(self, rhs: Indent) -> Indent {
94 block_indent: self.block_indent + rhs.block_indent,
95 alignment: self.alignment + rhs.alignment,
100 impl Sub for Indent {
101 type Output = Indent;
103 fn sub(self, rhs: Indent) -> Indent {
105 self.block_indent - rhs.block_indent,
106 self.alignment - rhs.alignment,
111 impl Add<usize> for Indent {
112 type Output = Indent;
114 fn add(self, rhs: usize) -> Indent {
115 Indent::new(self.block_indent, self.alignment + rhs)
119 impl Sub<usize> for Indent {
120 type Output = Indent;
122 fn sub(self, rhs: usize) -> Indent {
123 Indent::new(self.block_indent, self.alignment - rhs)
127 #[derive(Copy, Clone, Debug)]
130 // The current indentation of code.
132 // Indentation + any already emitted text on the first line of the current
138 /// `indent` is the indentation of the first line. The next lines
139 /// should begin with at least `indent` spaces (except backwards
140 /// indentation). The first line should not begin with indentation.
141 /// `width` is the maximum number of characters on the last line
142 /// (excluding `indent`). The width of other lines is not limited by
144 /// Note that in reality, we sometimes use width for lines other than the
145 /// last (i.e., we are conservative).
150 // |<------------>| max width
153 pub fn legacy(width: usize, indent: Indent) -> Shape {
157 offset: indent.alignment,
161 pub fn indented(indent: Indent, config: &Config) -> Shape {
163 width: config.max_width().checked_sub(indent.width()).unwrap_or(0),
165 offset: indent.alignment,
169 pub fn with_max_width(&self, config: &Config) -> Shape {
173 .checked_sub(self.indent.width())
179 pub fn offset(width: usize, indent: Indent, offset: usize) -> Shape {
187 pub fn visual_indent(&self, extra_width: usize) -> Shape {
188 let alignment = self.offset + extra_width;
191 indent: Indent::new(self.indent.block_indent, alignment),
196 pub fn block_indent(&self, extra_width: usize) -> Shape {
197 if self.indent.alignment == 0 {
200 indent: Indent::new(self.indent.block_indent + extra_width, 0),
206 indent: self.indent + extra_width,
207 offset: self.indent.alignment + extra_width,
212 pub fn block_left(&self, width: usize) -> Option<Shape> {
213 self.block_indent(width).sub_width(width)
216 pub fn add_offset(&self, extra_width: usize) -> Shape {
218 offset: self.offset + extra_width,
223 pub fn block(&self) -> Shape {
225 indent: self.indent.block_only(),
230 pub fn sub_width(&self, width: usize) -> Option<Shape> {
232 width: self.width.checked_sub(width)?,
237 pub fn shrink_left(&self, width: usize) -> Option<Shape> {
239 width: self.width.checked_sub(width)?,
240 indent: self.indent + width,
241 offset: self.offset + width,
245 pub fn offset_left(&self, width: usize) -> Option<Shape> {
246 self.add_offset(width).sub_width(width)
249 pub fn used_width(&self) -> usize {
250 self.indent.block_indent + self.offset
253 pub fn rhs_overhead(&self, config: &Config) -> usize {
256 .checked_sub(self.used_width() + self.width)
266 fn indent_add_sub() {
267 let indent = Indent::new(4, 8) + Indent::new(8, 12);
268 assert_eq!(12, indent.block_indent);
269 assert_eq!(20, indent.alignment);
271 let indent = indent - Indent::new(4, 4);
272 assert_eq!(8, indent.block_indent);
273 assert_eq!(16, indent.alignment);
277 fn indent_add_sub_alignment() {
278 let indent = Indent::new(4, 8) + 4;
279 assert_eq!(4, indent.block_indent);
280 assert_eq!(12, indent.alignment);
282 let indent = indent - 4;
283 assert_eq!(4, indent.block_indent);
284 assert_eq!(8, indent.alignment);
288 fn indent_to_string_spaces() {
289 let config = Config::default();
290 let indent = Indent::new(4, 8);
293 assert_eq!(" ", indent.to_string(&config));
297 fn indent_to_string_hard_tabs() {
298 let mut config = Config::default();
299 config.set().hard_tabs(true);
300 let indent = Indent::new(8, 4);
303 assert_eq!("\t\t ", indent.to_string(&config));
307 fn shape_visual_indent() {
308 let config = Config::default();
309 let indent = Indent::new(4, 8);
310 let shape = Shape::legacy(config.max_width(), indent);
311 let shape = shape.visual_indent(20);
313 assert_eq!(config.max_width(), shape.width);
314 assert_eq!(4, shape.indent.block_indent);
315 assert_eq!(28, shape.indent.alignment);
316 assert_eq!(28, shape.offset);
320 fn shape_block_indent_without_alignment() {
321 let config = Config::default();
322 let indent = Indent::new(4, 0);
323 let shape = Shape::legacy(config.max_width(), indent);
324 let shape = shape.block_indent(20);
326 assert_eq!(config.max_width(), shape.width);
327 assert_eq!(24, shape.indent.block_indent);
328 assert_eq!(0, shape.indent.alignment);
329 assert_eq!(0, shape.offset);
333 fn shape_block_indent_with_alignment() {
334 let config = Config::default();
335 let indent = Indent::new(4, 8);
336 let shape = Shape::legacy(config.max_width(), indent);
337 let shape = shape.block_indent(20);
339 assert_eq!(config.max_width(), shape.width);
340 assert_eq!(4, shape.indent.block_indent);
341 assert_eq!(28, shape.indent.alignment);
342 assert_eq!(28, shape.offset);