1 // Adapted from https://github.com/rust-lang/rust/blob/1.57.0/compiler/rustc_ast_pretty/src/pp.rs. 2 // See "Algorithm notes" in the crate-level rustdoc. 3 4 use crate::ring::RingBuffer; 5 use crate::{MARGIN, MIN_SPACE}; 6 use std::borrow::Cow; 7 use std::cmp; 8 use std::collections::VecDeque; 9 use std::iter; 10 11 #[derive(Clone, Copy, PartialEq)] 12 pub enum Breaks { 13 Consistent, 14 Inconsistent, 15 } 16 17 #[derive(Clone, Copy, Default)] 18 pub struct BreakToken { 19 pub offset: isize, 20 pub blank_space: usize, 21 pub pre_break: Option<char>, 22 pub post_break: &'static str, 23 pub no_break: Option<char>, 24 pub if_nonempty: bool, 25 pub never_break: bool, 26 } 27 28 #[derive(Clone, Copy)] 29 pub struct BeginToken { 30 pub offset: isize, 31 pub breaks: Breaks, 32 } 33 34 #[derive(Clone)] 35 pub enum Token { 36 String(Cow<'static, str>), 37 Break(BreakToken), 38 Begin(BeginToken), 39 End, 40 } 41 42 #[derive(Copy, Clone)] 43 enum PrintFrame { 44 Fits(Breaks), 45 Broken(usize, Breaks), 46 } 47 48 pub const SIZE_INFINITY: isize = 0xffff; 49 50 pub struct Printer { 51 out: String, 52 // Number of spaces left on line 53 space: isize, 54 // Ring-buffer of tokens and calculated sizes 55 buf: RingBuffer<BufEntry>, 56 // Total size of tokens already printed 57 left_total: isize, 58 // Total size of tokens enqueued, including printed and not yet printed 59 right_total: isize, 60 // Holds the ring-buffer index of the Begin that started the current block, 61 // possibly with the most recent Break after that Begin (if there is any) on 62 // top of it. Values are pushed and popped on the back of the queue using it 63 // like stack, and elsewhere old values are popped from the front of the 64 // queue as they become irrelevant due to the primary ring-buffer advancing. 65 scan_stack: VecDeque<usize>, 66 // Stack of blocks-in-progress being flushed by print 67 print_stack: Vec<PrintFrame>, 68 // Level of indentation of current line 69 indent: usize, 70 // Buffered indentation to avoid writing trailing whitespace 71 pending_indentation: usize, 72 } 73 74 #[derive(Clone)] 75 struct BufEntry { 76 token: Token, 77 size: isize, 78 } 79 80 impl Printer { new() -> Self81 pub fn new() -> Self { 82 Printer { 83 out: String::new(), 84 space: MARGIN, 85 buf: RingBuffer::new(), 86 left_total: 0, 87 right_total: 0, 88 scan_stack: VecDeque::new(), 89 print_stack: Vec::new(), 90 indent: 0, 91 pending_indentation: 0, 92 } 93 } 94 eof(mut self) -> String95 pub fn eof(mut self) -> String { 96 if !self.scan_stack.is_empty() { 97 self.check_stack(0); 98 self.advance_left(); 99 } 100 self.out 101 } 102 scan_begin(&mut self, token: BeginToken)103 pub fn scan_begin(&mut self, token: BeginToken) { 104 if self.scan_stack.is_empty() { 105 self.left_total = 1; 106 self.right_total = 1; 107 self.buf.clear(); 108 } 109 let right = self.buf.push(BufEntry { 110 token: Token::Begin(token), 111 size: -self.right_total, 112 }); 113 self.scan_stack.push_back(right); 114 } 115 scan_end(&mut self)116 pub fn scan_end(&mut self) { 117 if self.scan_stack.is_empty() { 118 self.print_end(); 119 } else { 120 if !self.buf.is_empty() { 121 if let Token::Break(break_token) = self.buf.last().token { 122 if self.buf.len() >= 2 { 123 if let Token::Begin(_) = self.buf.second_last().token { 124 self.buf.pop_last(); 125 self.buf.pop_last(); 126 self.scan_stack.pop_back(); 127 self.scan_stack.pop_back(); 128 self.right_total -= break_token.blank_space as isize; 129 return; 130 } 131 } 132 if break_token.if_nonempty { 133 self.buf.pop_last(); 134 self.scan_stack.pop_back(); 135 self.right_total -= break_token.blank_space as isize; 136 } 137 } 138 } 139 let right = self.buf.push(BufEntry { 140 token: Token::End, 141 size: -1, 142 }); 143 self.scan_stack.push_back(right); 144 } 145 } 146 scan_break(&mut self, token: BreakToken)147 pub fn scan_break(&mut self, token: BreakToken) { 148 if self.scan_stack.is_empty() { 149 self.left_total = 1; 150 self.right_total = 1; 151 self.buf.clear(); 152 } else { 153 self.check_stack(0); 154 } 155 let right = self.buf.push(BufEntry { 156 token: Token::Break(token), 157 size: -self.right_total, 158 }); 159 self.scan_stack.push_back(right); 160 self.right_total += token.blank_space as isize; 161 } 162 scan_string(&mut self, string: Cow<'static, str>)163 pub fn scan_string(&mut self, string: Cow<'static, str>) { 164 if self.scan_stack.is_empty() { 165 self.print_string(string); 166 } else { 167 let len = string.len() as isize; 168 self.buf.push(BufEntry { 169 token: Token::String(string), 170 size: len, 171 }); 172 self.right_total += len; 173 self.check_stream(); 174 } 175 } 176 177 #[track_caller] offset(&mut self, offset: isize)178 pub fn offset(&mut self, offset: isize) { 179 match &mut self.buf.last_mut().token { 180 Token::Break(token) => token.offset += offset, 181 Token::Begin(_) => {} 182 Token::String(_) | Token::End => unreachable!(), 183 } 184 } 185 end_with_max_width(&mut self, max: isize)186 pub fn end_with_max_width(&mut self, max: isize) { 187 let mut depth = 1; 188 for &index in self.scan_stack.iter().rev() { 189 let entry = &self.buf[index]; 190 match entry.token { 191 Token::Begin(_) => { 192 depth -= 1; 193 if depth == 0 { 194 if entry.size < 0 { 195 let actual_width = entry.size + self.right_total; 196 if actual_width > max { 197 self.buf.push(BufEntry { 198 token: Token::String(Cow::Borrowed("")), 199 size: SIZE_INFINITY, 200 }); 201 self.right_total += SIZE_INFINITY; 202 } 203 } 204 break; 205 } 206 } 207 Token::End => depth += 1, 208 Token::Break(_) => {} 209 Token::String(_) => unreachable!(), 210 } 211 } 212 self.scan_end(); 213 } 214 ends_with(&self, ch: char) -> bool215 pub fn ends_with(&self, ch: char) -> bool { 216 for i in self.buf.index_range().rev() { 217 if let Token::String(token) = &self.buf[i].token { 218 return token.ends_with(ch); 219 } 220 } 221 self.out.ends_with(ch) 222 } 223 check_stream(&mut self)224 fn check_stream(&mut self) { 225 while self.right_total - self.left_total > self.space { 226 if *self.scan_stack.front().unwrap() == self.buf.index_range().start { 227 self.scan_stack.pop_front().unwrap(); 228 self.buf.first_mut().size = SIZE_INFINITY; 229 } 230 231 self.advance_left(); 232 233 if self.buf.is_empty() { 234 break; 235 } 236 } 237 } 238 advance_left(&mut self)239 fn advance_left(&mut self) { 240 while self.buf.first().size >= 0 { 241 let left = self.buf.pop_first(); 242 243 match left.token { 244 Token::String(string) => { 245 self.left_total += left.size; 246 self.print_string(string); 247 } 248 Token::Break(token) => { 249 self.left_total += token.blank_space as isize; 250 self.print_break(token, left.size); 251 } 252 Token::Begin(token) => self.print_begin(token, left.size), 253 Token::End => self.print_end(), 254 } 255 256 if self.buf.is_empty() { 257 break; 258 } 259 } 260 } 261 check_stack(&mut self, mut depth: usize)262 fn check_stack(&mut self, mut depth: usize) { 263 while let Some(&index) = self.scan_stack.back() { 264 let entry = &mut self.buf[index]; 265 match entry.token { 266 Token::Begin(_) => { 267 if depth == 0 { 268 break; 269 } 270 self.scan_stack.pop_back().unwrap(); 271 entry.size += self.right_total; 272 depth -= 1; 273 } 274 Token::End => { 275 self.scan_stack.pop_back().unwrap(); 276 entry.size = 1; 277 depth += 1; 278 } 279 Token::Break(_) => { 280 self.scan_stack.pop_back().unwrap(); 281 entry.size += self.right_total; 282 if depth == 0 { 283 break; 284 } 285 } 286 Token::String(_) => unreachable!(), 287 } 288 } 289 } 290 get_top(&self) -> PrintFrame291 fn get_top(&self) -> PrintFrame { 292 const OUTER: PrintFrame = PrintFrame::Broken(0, Breaks::Inconsistent); 293 self.print_stack.last().map_or(OUTER, PrintFrame::clone) 294 } 295 print_begin(&mut self, token: BeginToken, size: isize)296 fn print_begin(&mut self, token: BeginToken, size: isize) { 297 if cfg!(prettyplease_debug) { 298 self.out.push(match token.breaks { 299 Breaks::Consistent => '«', 300 Breaks::Inconsistent => '‹', 301 }); 302 if cfg!(prettyplease_debug_indent) { 303 self.out 304 .extend(token.offset.to_string().chars().map(|ch| match ch { 305 '0'..='9' => ['₀', '₁', '₂', '₃', '₄', '₅', '₆', '₇', '₈', '₉'] 306 [(ch as u8 - b'0') as usize], 307 '-' => '₋', 308 _ => unreachable!(), 309 })); 310 } 311 } 312 if size > self.space { 313 self.print_stack 314 .push(PrintFrame::Broken(self.indent, token.breaks)); 315 self.indent = usize::try_from(self.indent as isize + token.offset).unwrap(); 316 } else { 317 self.print_stack.push(PrintFrame::Fits(token.breaks)); 318 } 319 } 320 print_end(&mut self)321 fn print_end(&mut self) { 322 let breaks = match self.print_stack.pop().unwrap() { 323 PrintFrame::Broken(indent, breaks) => { 324 self.indent = indent; 325 breaks 326 } 327 PrintFrame::Fits(breaks) => breaks, 328 }; 329 if cfg!(prettyplease_debug) { 330 self.out.push(match breaks { 331 Breaks::Consistent => '»', 332 Breaks::Inconsistent => '›', 333 }); 334 } 335 } 336 print_break(&mut self, token: BreakToken, size: isize)337 fn print_break(&mut self, token: BreakToken, size: isize) { 338 let fits = token.never_break 339 || match self.get_top() { 340 PrintFrame::Fits(..) => true, 341 PrintFrame::Broken(.., Breaks::Consistent) => false, 342 PrintFrame::Broken(.., Breaks::Inconsistent) => size <= self.space, 343 }; 344 if fits { 345 self.pending_indentation += token.blank_space; 346 self.space -= token.blank_space as isize; 347 if let Some(no_break) = token.no_break { 348 self.out.push(no_break); 349 self.space -= no_break.len_utf8() as isize; 350 } 351 if cfg!(prettyplease_debug) { 352 self.out.push('·'); 353 } 354 } else { 355 if let Some(pre_break) = token.pre_break { 356 self.print_indent(); 357 self.out.push(pre_break); 358 } 359 if cfg!(prettyplease_debug) { 360 self.out.push('·'); 361 } 362 self.out.push('\n'); 363 let indent = self.indent as isize + token.offset; 364 self.pending_indentation = usize::try_from(indent).unwrap(); 365 self.space = cmp::max(MARGIN - indent, MIN_SPACE); 366 if !token.post_break.is_empty() { 367 self.print_indent(); 368 self.out.push_str(token.post_break); 369 self.space -= token.post_break.len() as isize; 370 } 371 } 372 } 373 print_string(&mut self, string: Cow<'static, str>)374 fn print_string(&mut self, string: Cow<'static, str>) { 375 self.print_indent(); 376 self.out.push_str(&string); 377 self.space -= string.len() as isize; 378 } 379 print_indent(&mut self)380 fn print_indent(&mut self) { 381 self.out.reserve(self.pending_indentation); 382 self.out 383 .extend(iter::repeat(' ').take(self.pending_indentation)); 384 self.pending_indentation = 0; 385 } 386 } 387