]> git.proxmox.com Git - rustc.git/blame - src/tools/rustfmt/src/shape.rs
Update upstream source from tag 'upstream/1.52.1+dfsg1'
[rustc.git] / src / tools / rustfmt / src / shape.rs
CommitLineData
f20569fa
XL
1use std::borrow::Cow;
2use std::cmp::min;
3use std::ops::{Add, Sub};
4
5use crate::Config;
6
7#[derive(Copy, Clone, Debug)]
8pub(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
17const INDENT_BUFFER_LEN: usize = 80;
18const INDENT_BUFFER: &str =
19 "\n ";
20
21impl Indent {
22 pub(crate) fn new(block_indent: usize, alignment: usize) -> Indent {
23 Indent {
24 block_indent,
25 alignment,
26 }
27 }
28
29 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
39 pub(crate) fn empty() -> Indent {
40 Indent::new(0, 0)
41 }
42
43 pub(crate) fn block_only(&self) -> Indent {
44 Indent {
45 block_indent: self.block_indent,
46 alignment: 0,
47 }
48 }
49
50 pub(crate) fn block_indent(mut self, config: &Config) -> Indent {
51 self.block_indent += config.tab_spaces();
52 self
53 }
54
55 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
64 pub(crate) fn width(&self) -> usize {
65 self.block_indent + self.alignment
66 }
67
68 pub(crate) fn to_string(&self, config: &Config) -> Cow<'static, str> {
69 self.to_string_inner(config, 1)
70 }
71
72 pub(crate) fn to_string_with_newline(&self, config: &Config) -> Cow<'static, str> {
73 self.to_string_inner(config, 0)
74 }
75
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
101impl Add for Indent {
102 type Output = Indent;
103
104 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
112impl Sub for Indent {
113 type Output = Indent;
114
115 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
123impl Add<usize> for Indent {
124 type Output = Indent;
125
126 fn add(self, rhs: usize) -> Indent {
127 Indent::new(self.block_indent, self.alignment + rhs)
128 }
129}
130
131impl Sub<usize> for Indent {
132 type Output = Indent;
133
134 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.
140const INFINITE_SHAPE_WIDTH: usize = 8096;
141
142#[derive(Copy, Clone, Debug)]
143pub(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
152impl 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
168 pub(crate) fn legacy(width: usize, indent: Indent) -> Shape {
169 Shape {
170 width,
171 indent,
172 offset: indent.alignment,
173 }
174 }
175
176 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
184 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
191 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
200 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
216 pub(crate) fn block_left(&self, width: usize) -> Option<Shape> {
217 self.block_indent(width).sub_width(width)
218 }
219
220 pub(crate) fn add_offset(&self, extra_width: usize) -> Shape {
221 Shape {
222 offset: self.offset + extra_width,
223 ..*self
224 }
225 }
226
227 pub(crate) fn block(&self) -> Shape {
228 Shape {
229 indent: self.indent.block_only(),
230 ..*self
231 }
232 }
233
234 pub(crate) fn saturating_sub_width(&self, width: usize) -> Shape {
235 self.sub_width(width).unwrap_or(Shape { width: 0, ..*self })
236 }
237
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
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
253 pub(crate) fn offset_left(&self, width: usize) -> Option<Shape> {
254 self.add_offset(width).sub_width(width)
255 }
256
257 pub(crate) fn used_width(&self) -> usize {
258 self.indent.block_indent + self.offset
259 }
260
261 pub(crate) fn rhs_overhead(&self, config: &Config) -> usize {
262 config
263 .max_width()
264 .saturating_sub(self.used_width() + self.width)
265 }
266
267 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
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.
282 pub(crate) fn infinite_width(&self) -> Shape {
283 Shape {
284 width: INFINITE_SHAPE_WIDTH,
285 ..*self
286 }
287 }
288}
289
290#[cfg(test)]
291mod test {
292 use super::*;
293
294 #[test]
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]
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]
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]
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]
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]
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]
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}