]> git.lizzy.rs Git - rust.git/blob - src/shape.rs
Cargo update (#2602)
[rust.git] / src / shape.rs
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.
4 //
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.
10
11 use std::borrow::Cow;
12 use std::cmp::min;
13 use std::ops::{Add, Sub};
14
15 use Config;
16
17 #[derive(Copy, Clone, Debug)]
18 pub struct Indent {
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.
23     pub alignment: usize,
24 }
25
26 // INDENT_BUFFER.len() = 81
27 const INDENT_BUFFER_LEN: usize = 80;
28 const INDENT_BUFFER: &str =
29     "\n                                                                                ";
30 impl Indent {
31     pub fn new(block_indent: usize, alignment: usize) -> Indent {
32         Indent {
33             block_indent,
34             alignment,
35         }
36     }
37
38     pub fn from_width(config: &Config, width: usize) -> Indent {
39         if config.hard_tabs() {
40             let tab_num = width / config.tab_spaces();
41             let alignment = width % config.tab_spaces();
42             Indent::new(config.tab_spaces() * tab_num, alignment)
43         } else {
44             Indent::new(width, 0)
45         }
46     }
47
48     pub fn empty() -> Indent {
49         Indent::new(0, 0)
50     }
51
52     pub fn block_only(&self) -> Indent {
53         Indent {
54             block_indent: self.block_indent,
55             alignment: 0,
56         }
57     }
58
59     pub fn block_indent(mut self, config: &Config) -> Indent {
60         self.block_indent += config.tab_spaces();
61         self
62     }
63
64     pub fn block_unindent(mut self, config: &Config) -> Indent {
65         if self.block_indent < config.tab_spaces() {
66             Indent::new(self.block_indent, 0)
67         } else {
68             self.block_indent -= config.tab_spaces();
69             self
70         }
71     }
72
73     pub fn width(&self) -> usize {
74         self.block_indent + self.alignment
75     }
76
77     pub fn to_string(&self, config: &Config) -> Cow<'static, str> {
78         self.to_string_inner(config, 1)
79     }
80
81     pub fn to_string_with_newline(&self, config: &Config) -> Cow<'static, str> {
82         self.to_string_inner(config, 0)
83     }
84
85     fn to_string_inner(&self, config: &Config, offset: usize) -> Cow<'static, str> {
86         let (num_tabs, num_spaces) = if config.hard_tabs() {
87             (self.block_indent / config.tab_spaces(), self.alignment)
88         } else {
89             (0, self.width())
90         };
91         let num_chars = num_tabs + num_spaces;
92         if num_tabs == 0 && num_chars + offset <= INDENT_BUFFER_LEN {
93             Cow::from(&INDENT_BUFFER[offset..num_chars + 1])
94         } else {
95             let mut indent = String::with_capacity(num_chars + if offset == 0 { 1 } else { 0 });
96             if offset == 0 {
97                 indent.push('\n');
98             }
99             for _ in 0..num_tabs {
100                 indent.push('\t')
101             }
102             for _ in 0..num_spaces {
103                 indent.push(' ')
104             }
105             Cow::from(indent)
106         }
107     }
108 }
109
110 impl Add for Indent {
111     type Output = Indent;
112
113     fn add(self, rhs: Indent) -> Indent {
114         Indent {
115             block_indent: self.block_indent + rhs.block_indent,
116             alignment: self.alignment + rhs.alignment,
117         }
118     }
119 }
120
121 impl Sub for Indent {
122     type Output = Indent;
123
124     fn sub(self, rhs: Indent) -> Indent {
125         Indent::new(
126             self.block_indent - rhs.block_indent,
127             self.alignment - rhs.alignment,
128         )
129     }
130 }
131
132 impl Add<usize> for Indent {
133     type Output = Indent;
134
135     fn add(self, rhs: usize) -> Indent {
136         Indent::new(self.block_indent, self.alignment + rhs)
137     }
138 }
139
140 impl Sub<usize> for Indent {
141     type Output = Indent;
142
143     fn sub(self, rhs: usize) -> Indent {
144         Indent::new(self.block_indent, self.alignment - rhs)
145     }
146 }
147
148 #[derive(Copy, Clone, Debug)]
149 pub struct Shape {
150     pub width: usize,
151     // The current indentation of code.
152     pub indent: Indent,
153     // Indentation + any already emitted text on the first line of the current
154     // statement.
155     pub offset: usize,
156 }
157
158 impl Shape {
159     /// `indent` is the indentation of the first line. The next lines
160     /// should begin with at least `indent` spaces (except backwards
161     /// indentation). The first line should not begin with indentation.
162     /// `width` is the maximum number of characters on the last line
163     /// (excluding `indent`). The width of other lines is not limited by
164     /// `width`.
165     /// Note that in reality, we sometimes use width for lines other than the
166     /// last (i.e., we are conservative).
167     // .......*-------*
168     //        |       |
169     //        |     *-*
170     //        *-----|
171     // |<------------>|  max width
172     // |<---->|          indent
173     //        |<--->|    width
174     pub fn legacy(width: usize, indent: Indent) -> Shape {
175         Shape {
176             width,
177             indent,
178             offset: indent.alignment,
179         }
180     }
181
182     pub fn indented(indent: Indent, config: &Config) -> Shape {
183         Shape {
184             width: config.max_width().checked_sub(indent.width()).unwrap_or(0),
185             indent,
186             offset: indent.alignment,
187         }
188     }
189
190     pub fn with_max_width(&self, config: &Config) -> Shape {
191         Shape {
192             width: config
193                 .max_width()
194                 .checked_sub(self.indent.width())
195                 .unwrap_or(0),
196             ..*self
197         }
198     }
199
200     pub fn offset(width: usize, indent: Indent, offset: usize) -> Shape {
201         Shape {
202             width,
203             indent,
204             offset,
205         }
206     }
207
208     pub fn visual_indent(&self, extra_width: usize) -> Shape {
209         let alignment = self.offset + extra_width;
210         Shape {
211             width: self.width,
212             indent: Indent::new(self.indent.block_indent, alignment),
213             offset: alignment,
214         }
215     }
216
217     pub fn block_indent(&self, extra_width: usize) -> Shape {
218         if self.indent.alignment == 0 {
219             Shape {
220                 width: self.width,
221                 indent: Indent::new(self.indent.block_indent + extra_width, 0),
222                 offset: 0,
223             }
224         } else {
225             Shape {
226                 width: self.width,
227                 indent: self.indent + extra_width,
228                 offset: self.indent.alignment + extra_width,
229             }
230         }
231     }
232
233     pub fn block_left(&self, width: usize) -> Option<Shape> {
234         self.block_indent(width).sub_width(width)
235     }
236
237     pub fn add_offset(&self, extra_width: usize) -> Shape {
238         Shape {
239             offset: self.offset + extra_width,
240             ..*self
241         }
242     }
243
244     pub fn block(&self) -> Shape {
245         Shape {
246             indent: self.indent.block_only(),
247             ..*self
248         }
249     }
250
251     pub fn sub_width(&self, width: usize) -> Option<Shape> {
252         Some(Shape {
253             width: self.width.checked_sub(width)?,
254             ..*self
255         })
256     }
257
258     pub fn shrink_left(&self, width: usize) -> Option<Shape> {
259         Some(Shape {
260             width: self.width.checked_sub(width)?,
261             indent: self.indent + width,
262             offset: self.offset + width,
263         })
264     }
265
266     pub fn offset_left(&self, width: usize) -> Option<Shape> {
267         self.add_offset(width).sub_width(width)
268     }
269
270     pub fn used_width(&self) -> usize {
271         self.indent.block_indent + self.offset
272     }
273
274     pub fn rhs_overhead(&self, config: &Config) -> usize {
275         config
276             .max_width()
277             .checked_sub(self.used_width() + self.width)
278             .unwrap_or(0)
279     }
280
281     pub fn comment(&self, config: &Config) -> Shape {
282         let width = min(
283             self.width,
284             config
285                 .comment_width()
286                 .checked_sub(self.indent.width())
287                 .unwrap_or(0),
288         );
289         Shape { width, ..*self }
290     }
291 }
292
293 #[cfg(test)]
294 mod test {
295     use super::*;
296
297     #[test]
298     fn indent_add_sub() {
299         let indent = Indent::new(4, 8) + Indent::new(8, 12);
300         assert_eq!(12, indent.block_indent);
301         assert_eq!(20, indent.alignment);
302
303         let indent = indent - Indent::new(4, 4);
304         assert_eq!(8, indent.block_indent);
305         assert_eq!(16, indent.alignment);
306     }
307
308     #[test]
309     fn indent_add_sub_alignment() {
310         let indent = Indent::new(4, 8) + 4;
311         assert_eq!(4, indent.block_indent);
312         assert_eq!(12, indent.alignment);
313
314         let indent = indent - 4;
315         assert_eq!(4, indent.block_indent);
316         assert_eq!(8, indent.alignment);
317     }
318
319     #[test]
320     fn indent_to_string_spaces() {
321         let config = Config::default();
322         let indent = Indent::new(4, 8);
323
324         // 12 spaces
325         assert_eq!("            ", indent.to_string(&config));
326     }
327
328     #[test]
329     fn indent_to_string_hard_tabs() {
330         let mut config = Config::default();
331         config.set().hard_tabs(true);
332         let indent = Indent::new(8, 4);
333
334         // 2 tabs + 4 spaces
335         assert_eq!("\t\t    ", indent.to_string(&config));
336     }
337
338     #[test]
339     fn shape_visual_indent() {
340         let config = Config::default();
341         let indent = Indent::new(4, 8);
342         let shape = Shape::legacy(config.max_width(), indent);
343         let shape = shape.visual_indent(20);
344
345         assert_eq!(config.max_width(), shape.width);
346         assert_eq!(4, shape.indent.block_indent);
347         assert_eq!(28, shape.indent.alignment);
348         assert_eq!(28, shape.offset);
349     }
350
351     #[test]
352     fn shape_block_indent_without_alignment() {
353         let config = Config::default();
354         let indent = Indent::new(4, 0);
355         let shape = Shape::legacy(config.max_width(), indent);
356         let shape = shape.block_indent(20);
357
358         assert_eq!(config.max_width(), shape.width);
359         assert_eq!(24, shape.indent.block_indent);
360         assert_eq!(0, shape.indent.alignment);
361         assert_eq!(0, shape.offset);
362     }
363
364     #[test]
365     fn shape_block_indent_with_alignment() {
366         let config = Config::default();
367         let indent = Indent::new(4, 8);
368         let shape = Shape::legacy(config.max_width(), indent);
369         let shape = shape.block_indent(20);
370
371         assert_eq!(config.max_width(), shape.width);
372         assert_eq!(4, shape.indent.block_indent);
373         assert_eq!(28, shape.indent.alignment);
374         assert_eq!(28, shape.offset);
375     }
376 }