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.
13 use std::ops::{Add, Sub};
17 #[derive(Copy, Clone, Debug)]
19 // Width of the block indent, in characters. Must be a multiple of
20 // Config::tab_spaces.
21 pub block_indent: usize,
22 // Alignment in characters.
26 // INDENT_BUFFER.len() = 81
27 const INDENT_BUFFER_LEN: usize = 80;
28 const INDENT_BUFFER: &str =
32 pub fn new(block_indent: usize, alignment: usize) -> Indent {
39 pub fn from_width(config: &Config, width: usize) -> Indent {
40 if config.hard_tabs() {
41 let tab_num = width / config.tab_spaces();
42 let alignment = width % config.tab_spaces();
43 Indent::new(config.tab_spaces() * tab_num, alignment)
49 pub fn empty() -> Indent {
53 pub fn block_only(&self) -> Indent {
55 block_indent: self.block_indent,
60 pub fn block_indent(mut self, config: &Config) -> Indent {
61 self.block_indent += config.tab_spaces();
65 pub fn block_unindent(mut self, config: &Config) -> Indent {
66 if self.block_indent < config.tab_spaces() {
67 Indent::new(self.block_indent, 0)
69 self.block_indent -= config.tab_spaces();
74 pub fn width(&self) -> usize {
75 self.block_indent + self.alignment
78 pub fn to_string(&self, config: &Config) -> Cow<'static, str> {
79 self.to_string_inner(config, 1)
82 pub fn to_string_with_newline(&self, config: &Config) -> Cow<'static, str> {
83 self.to_string_inner(config, 0)
86 fn to_string_inner(&self, config: &Config, offset: usize) -> Cow<'static, str> {
87 let (num_tabs, num_spaces) = if config.hard_tabs() {
88 (self.block_indent / config.tab_spaces(), self.alignment)
92 let num_chars = num_tabs + num_spaces;
93 if num_tabs == 0 && num_chars + offset <= INDENT_BUFFER_LEN {
94 Cow::from(&INDENT_BUFFER[offset..=num_chars])
96 let mut indent = String::with_capacity(num_chars + if offset == 0 { 1 } else { 0 });
100 for _ in 0..num_tabs {
103 for _ in 0..num_spaces {
111 impl Add for Indent {
112 type Output = Indent;
114 fn add(self, rhs: Indent) -> Indent {
116 block_indent: self.block_indent + rhs.block_indent,
117 alignment: self.alignment + rhs.alignment,
122 impl Sub for Indent {
123 type Output = Indent;
125 fn sub(self, rhs: Indent) -> Indent {
127 self.block_indent - rhs.block_indent,
128 self.alignment - rhs.alignment,
133 impl Add<usize> for Indent {
134 type Output = Indent;
136 fn add(self, rhs: usize) -> Indent {
137 Indent::new(self.block_indent, self.alignment + rhs)
141 impl Sub<usize> for Indent {
142 type Output = Indent;
144 fn sub(self, rhs: usize) -> Indent {
145 Indent::new(self.block_indent, self.alignment - rhs)
149 #[derive(Copy, Clone, Debug)]
152 // The current indentation of code.
154 // Indentation + any already emitted text on the first line of the current
160 /// `indent` is the indentation of the first line. The next lines
161 /// should begin with at least `indent` spaces (except backwards
162 /// indentation). The first line should not begin with indentation.
163 /// `width` is the maximum number of characters on the last line
164 /// (excluding `indent`). The width of other lines is not limited by
166 /// Note that in reality, we sometimes use width for lines other than the
167 /// last (i.e., we are conservative).
172 // |<------------>| max width
175 pub fn legacy(width: usize, indent: Indent) -> Shape {
179 offset: indent.alignment,
183 pub fn indented(indent: Indent, config: &Config) -> Shape {
185 width: config.max_width().saturating_sub(indent.width()),
187 offset: indent.alignment,
191 pub fn with_max_width(&self, config: &Config) -> Shape {
193 width: config.max_width().saturating_sub(self.indent.width()),
198 pub fn visual_indent(&self, extra_width: usize) -> Shape {
199 let alignment = self.offset + extra_width;
202 indent: Indent::new(self.indent.block_indent, alignment),
207 pub fn block_indent(&self, extra_width: usize) -> Shape {
208 if self.indent.alignment == 0 {
211 indent: Indent::new(self.indent.block_indent + extra_width, 0),
217 indent: self.indent + extra_width,
218 offset: self.indent.alignment + extra_width,
223 pub fn block_left(&self, width: usize) -> Option<Shape> {
224 self.block_indent(width).sub_width(width)
227 pub fn add_offset(&self, extra_width: usize) -> Shape {
229 offset: self.offset + extra_width,
234 pub fn block(&self) -> Shape {
236 indent: self.indent.block_only(),
241 pub fn sub_width(&self, width: usize) -> Option<Shape> {
243 width: self.width.checked_sub(width)?,
248 pub fn shrink_left(&self, width: usize) -> Option<Shape> {
250 width: self.width.checked_sub(width)?,
251 indent: self.indent + width,
252 offset: self.offset + width,
256 pub fn offset_left(&self, width: usize) -> Option<Shape> {
257 self.add_offset(width).sub_width(width)
260 pub fn used_width(&self) -> usize {
261 self.indent.block_indent + self.offset
264 pub fn rhs_overhead(&self, config: &Config) -> usize {
267 .saturating_sub(self.used_width() + self.width)
270 pub fn comment(&self, config: &Config) -> Shape {
273 config.comment_width().saturating_sub(self.indent.width()),
275 Shape { width, ..*self }
278 pub fn to_string_with_newline(&self, config: &Config) -> Cow<'static, str> {
279 let mut offset_indent = self.indent;
280 offset_indent.alignment = self.offset;
281 offset_indent.to_string_inner(config, 0)
290 fn indent_add_sub() {
291 let indent = Indent::new(4, 8) + Indent::new(8, 12);
292 assert_eq!(12, indent.block_indent);
293 assert_eq!(20, indent.alignment);
295 let indent = indent - Indent::new(4, 4);
296 assert_eq!(8, indent.block_indent);
297 assert_eq!(16, indent.alignment);
301 fn indent_add_sub_alignment() {
302 let indent = Indent::new(4, 8) + 4;
303 assert_eq!(4, indent.block_indent);
304 assert_eq!(12, indent.alignment);
306 let indent = indent - 4;
307 assert_eq!(4, indent.block_indent);
308 assert_eq!(8, indent.alignment);
312 fn indent_to_string_spaces() {
313 let config = Config::default();
314 let indent = Indent::new(4, 8);
317 assert_eq!(" ", indent.to_string(&config));
321 fn indent_to_string_hard_tabs() {
322 let mut config = Config::default();
323 config.set().hard_tabs(true);
324 let indent = Indent::new(8, 4);
327 assert_eq!("\t\t ", indent.to_string(&config));
331 fn shape_visual_indent() {
332 let config = Config::default();
333 let indent = Indent::new(4, 8);
334 let shape = Shape::legacy(config.max_width(), indent);
335 let shape = shape.visual_indent(20);
337 assert_eq!(config.max_width(), shape.width);
338 assert_eq!(4, shape.indent.block_indent);
339 assert_eq!(28, shape.indent.alignment);
340 assert_eq!(28, shape.offset);
344 fn shape_block_indent_without_alignment() {
345 let config = Config::default();
346 let indent = Indent::new(4, 0);
347 let shape = Shape::legacy(config.max_width(), indent);
348 let shape = shape.block_indent(20);
350 assert_eq!(config.max_width(), shape.width);
351 assert_eq!(24, shape.indent.block_indent);
352 assert_eq!(0, shape.indent.alignment);
353 assert_eq!(0, shape.offset);
357 fn shape_block_indent_with_alignment() {
358 let config = Config::default();
359 let indent = Indent::new(4, 8);
360 let shape = Shape::legacy(config.max_width(), indent);
361 let shape = shape.block_indent(20);
363 assert_eq!(config.max_width(), shape.width);
364 assert_eq!(4, shape.indent.block_indent);
365 assert_eq!(28, shape.indent.alignment);
366 assert_eq!(28, shape.offset);