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