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