1use std::borrow::Cow;
2use std::cmp::min;
3use std::ops::{Add, Sub};
4
5use rustc_span::Span;
6
7use crate::Config;
8use crate::rewrite::ExceedsMaxWidthError;
9
10#[derive(Copy, Clone, Debug)]
11pub(crate) struct Indent {
12 pub(crate) block_indent: usize,
15 pub(crate) alignment: usize,
17}
18
19const INDENT_BUFFER_LEN: usize = 80;
21const INDENT_BUFFER: &str =
22 "\n ";
23
24impl Indent {
25 pub(crate) fn new(block_indent: usize, alignment: usize) -> Indent {
26 Indent {
27 block_indent,
28 alignment,
29 }
30 }
31
32 pub(crate) 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)
37 } else {
38 Indent::new(width, 0)
39 }
40 }
41
42 pub(crate) fn empty() -> Indent {
43 Indent::new(0, 0)
44 }
45
46 pub(crate) fn block_only(&self) -> Indent {
47 Indent {
48 block_indent: self.block_indent,
49 alignment: 0,
50 }
51 }
52
53 pub(crate) fn block_indent(mut self, config: &Config) -> Indent {
54 self.block_indent += config.tab_spaces();
55 self
56 }
57
58 pub(crate) fn block_unindent(mut self, config: &Config) -> Indent {
59 if self.block_indent < config.tab_spaces() {
60 Indent::new(self.block_indent, 0)
61 } else {
62 self.block_indent -= config.tab_spaces();
63 self
64 }
65 }
66
67 pub(crate) fn width(&self) -> usize {
68 self.block_indent + self.alignment
69 }
70
71 pub(crate) fn to_string(&self, config: &Config) -> Cow<'static, str> {
72 self.to_string_inner(config, 1)
73 }
74
75 pub(crate) fn to_string_with_newline(&self, config: &Config) -> Cow<'static, str> {
76 self.to_string_inner(config, 0)
77 }
78
79 fn to_string_inner(&self, config: &Config, offset: usize) -> Cow<'static, str> {
80 let (num_tabs, num_spaces) = if config.hard_tabs() {
81 (self.block_indent / config.tab_spaces(), self.alignment)
82 } else {
83 (0, self.width())
84 };
85 let num_chars = num_tabs + num_spaces;
86 if num_tabs == 0 && num_chars + offset <= INDENT_BUFFER_LEN {
87 Cow::from(&INDENT_BUFFER[offset..=num_chars])
88 } else {
89 let mut indent = String::with_capacity(num_chars + if offset == 0 { 1 } else { 0 });
90 if offset == 0 {
91 indent.push('\n');
92 }
93 for _ in 0..num_tabs {
94 indent.push('\t');
95 }
96 for _ in 0..num_spaces {
97 indent.push(' ');
98 }
99 Cow::from(indent)
100 }
101 }
102}
103
104impl Add for Indent {
105 type Output = Indent;
106
107 fn add(self, rhs: Indent) -> Indent {
108 Indent {
109 block_indent: self.block_indent + rhs.block_indent,
110 alignment: self.alignment + rhs.alignment,
111 }
112 }
113}
114
115impl Sub for Indent {
116 type Output = Indent;
117
118 fn sub(self, rhs: Indent) -> Indent {
119 Indent::new(
120 self.block_indent - rhs.block_indent,
121 self.alignment - rhs.alignment,
122 )
123 }
124}
125
126impl Add<usize> for Indent {
127 type Output = Indent;
128
129 fn add(self, rhs: usize) -> Indent {
130 Indent::new(self.block_indent, self.alignment + rhs)
131 }
132}
133
134impl Sub<usize> for Indent {
135 type Output = Indent;
136
137 fn sub(self, rhs: usize) -> Indent {
138 Indent::new(self.block_indent, self.alignment - rhs)
139 }
140}
141
142const INFINITE_SHAPE_WIDTH: usize = 8096;
144
145#[derive(Copy, Clone, Debug)]
146pub(crate) struct Shape {
147 pub(crate) width: usize,
148 pub(crate) indent: Indent,
150 pub(crate) offset: usize,
153}
154
155impl Shape {
156 pub(crate) fn legacy(width: usize, indent: Indent) -> Shape {
172 Shape {
173 width,
174 indent,
175 offset: indent.alignment,
176 }
177 }
178
179 pub(crate) fn indented(indent: Indent, config: &Config) -> Shape {
180 Shape {
181 width: config.max_width().saturating_sub(indent.width()),
182 indent,
183 offset: indent.alignment,
184 }
185 }
186
187 pub(crate) fn with_max_width(&self, config: &Config) -> Shape {
188 Shape {
189 width: config.max_width().saturating_sub(self.indent.width()),
190 ..*self
191 }
192 }
193
194 pub(crate) fn visual_indent(&self, delta: usize) -> Shape {
195 let alignment = self.offset + delta;
196 Shape {
197 width: self.width,
198 indent: Indent::new(self.indent.block_indent, alignment),
199 offset: alignment,
200 }
201 }
202
203 pub(crate) fn block_indent(&self, delta: usize) -> Shape {
204 if self.indent.alignment == 0 {
205 Shape {
206 width: self.width,
207 indent: Indent::new(self.indent.block_indent + delta, 0),
208 offset: 0,
209 }
210 } else {
211 Shape {
212 width: self.width,
213 indent: self.indent + delta,
214 offset: self.indent.alignment + delta,
215 }
216 }
217 }
218
219 pub(crate) fn block_left(
220 &self,
221 delta: usize,
222 span: Span,
223 ) -> Result<Shape, ExceedsMaxWidthError> {
224 self.block_indent(delta).sub_width(delta, span)
225 }
226
227 pub(crate) fn add_offset(&self, delta: usize) -> Shape {
228 Shape {
229 offset: self.offset + delta,
230 ..*self
231 }
232 }
233
234 pub(crate) fn block(&self) -> Shape {
235 Shape {
236 indent: self.indent.block_only(),
237 ..*self
238 }
239 }
240
241 pub(crate) fn saturating_sub_width(&self, delta: usize) -> Shape {
242 Shape {
243 width: self.width.saturating_sub(delta),
244 ..*self
245 }
246 }
247
248 pub(crate) fn sub_width(
249 &self,
250 delta: usize,
251 span: Span,
252 ) -> Result<Shape, ExceedsMaxWidthError> {
253 self.sub_width_opt(delta)
254 .ok_or_else(|| self.exceeds_max_width_error(span))
255 }
256
257 pub(crate) fn sub_width_opt(&self, delta: usize) -> Option<Shape> {
258 self.width
259 .checked_sub(delta)
260 .map(|width| Shape { width, ..*self })
261 }
262
263 pub(crate) fn shrink_left(
264 &self,
265 delta: usize,
266 span: Span,
267 ) -> Result<Shape, ExceedsMaxWidthError> {
268 self.shrink_left_opt(delta)
269 .ok_or_else(|| self.exceeds_max_width_error(span))
270 }
271
272 pub(crate) fn shrink_left_opt(&self, delta: usize) -> Option<Shape> {
273 self.width.checked_sub(delta).map(|width| Shape {
274 width,
275 indent: self.indent + delta,
276 offset: self.offset + delta,
277 })
278 }
279
280 pub(crate) fn offset_left(
281 &self,
282 delta: usize,
283 span: Span,
284 ) -> Result<Shape, ExceedsMaxWidthError> {
285 self.offset_left_opt(delta)
286 .ok_or_else(|| self.exceeds_max_width_error(span))
287 }
288
289 pub(crate) fn offset_left_opt(&self, delta: usize) -> Option<Shape> {
290 self.add_offset(delta).sub_width_opt(delta)
291 }
292
293 pub(crate) fn used_width(&self) -> usize {
294 self.indent.block_indent + self.offset
295 }
296
297 pub(crate) fn rhs_overhead(&self, config: &Config) -> usize {
298 config
299 .max_width()
300 .saturating_sub(self.used_width() + self.width)
301 }
302
303 pub(crate) fn comment(&self, config: &Config) -> Shape {
304 let width = min(
305 self.width,
306 config.comment_width().saturating_sub(self.indent.width()),
307 );
308 Shape { width, ..*self }
309 }
310
311 pub(crate) fn to_string_with_newline(&self, config: &Config) -> Cow<'static, str> {
312 let mut offset_indent = self.indent;
313 offset_indent.alignment = self.offset;
314 offset_indent.to_string_inner(config, 0)
315 }
316
317 pub(crate) fn infinite_width(&self) -> Shape {
319 Shape {
320 width: INFINITE_SHAPE_WIDTH,
321 ..*self
322 }
323 }
324
325 fn exceeds_max_width_error(&self, span: Span) -> ExceedsMaxWidthError {
326 ExceedsMaxWidthError {
327 configured_width: self.width,
328 span,
329 }
330 }
331}
332
333#[cfg(test)]
334mod test {
335 use super::*;
336
337 #[test]
338 fn indent_add_sub() {
339 let indent = Indent::new(4, 8) + Indent::new(8, 12);
340 assert_eq!(12, indent.block_indent);
341 assert_eq!(20, indent.alignment);
342
343 let indent = indent - Indent::new(4, 4);
344 assert_eq!(8, indent.block_indent);
345 assert_eq!(16, indent.alignment);
346 }
347
348 #[test]
349 fn indent_add_sub_alignment() {
350 let indent = Indent::new(4, 8) + 4;
351 assert_eq!(4, indent.block_indent);
352 assert_eq!(12, indent.alignment);
353
354 let indent = indent - 4;
355 assert_eq!(4, indent.block_indent);
356 assert_eq!(8, indent.alignment);
357 }
358
359 #[test]
360 fn indent_to_string_spaces() {
361 let config = Config::default();
362 let indent = Indent::new(4, 8);
363
364 assert_eq!(" ", indent.to_string(&config));
366 }
367
368 #[test]
369 fn indent_to_string_hard_tabs() {
370 let mut config = Config::default();
371 config.set().hard_tabs(true);
372 let indent = Indent::new(8, 4);
373
374 assert_eq!("\t\t ", indent.to_string(&config));
376 }
377
378 #[test]
379 fn shape_visual_indent() {
380 let config = Config::default();
381 let indent = Indent::new(4, 8);
382 let shape = Shape::legacy(config.max_width(), indent);
383 let shape = shape.visual_indent(20);
384
385 assert_eq!(config.max_width(), shape.width);
386 assert_eq!(4, shape.indent.block_indent);
387 assert_eq!(28, shape.indent.alignment);
388 assert_eq!(28, shape.offset);
389 }
390
391 #[test]
392 fn shape_block_indent_without_alignment() {
393 let config = Config::default();
394 let indent = Indent::new(4, 0);
395 let shape = Shape::legacy(config.max_width(), indent);
396 let shape = shape.block_indent(20);
397
398 assert_eq!(config.max_width(), shape.width);
399 assert_eq!(24, shape.indent.block_indent);
400 assert_eq!(0, shape.indent.alignment);
401 assert_eq!(0, shape.offset);
402 }
403
404 #[test]
405 fn shape_block_indent_with_alignment() {
406 let config = Config::default();
407 let indent = Indent::new(4, 8);
408 let shape = Shape::legacy(config.max_width(), indent);
409 let shape = shape.block_indent(20);
410
411 assert_eq!(config.max_width(), shape.width);
412 assert_eq!(4, shape.indent.block_indent);
413 assert_eq!(28, shape.indent.alignment);
414 assert_eq!(28, shape.offset);
415 }
416}