]>
Commit | Line | Data |
---|---|---|
223e47cc LB |
1 | // Copyright 2012 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
1a4d82fc JJ |
11 | //! This pretty-printer is a direct reimplementation of Philip Karlton's |
12 | //! Mesa pretty-printer, as described in appendix A of | |
13 | //! | |
041b39d2 | 14 | //! ````text |
92a42be0 SL |
15 | //! STAN-CS-79-770: "Pretty Printing", by Derek C. Oppen. |
16 | //! Stanford Department of Computer Science, 1979. | |
17 | //! ```` | |
1a4d82fc JJ |
18 | //! |
19 | //! The algorithm's aim is to break a stream into as few lines as possible | |
20 | //! while respecting the indentation-consistency requirements of the enclosing | |
21 | //! block, and avoiding breaking at silly places on block boundaries, for | |
22 | //! example, between "x" and ")" in "x)". | |
23 | //! | |
24 | //! I am implementing this algorithm because it comes with 20 pages of | |
25 | //! documentation explaining its theory, and because it addresses the set of | |
26 | //! concerns I've seen other pretty-printers fall down on. Weirdly. Even though | |
27 | //! it's 32 years old. What can I say? | |
28 | //! | |
29 | //! Despite some redundancies and quirks in the way it's implemented in that | |
30 | //! paper, I've opted to keep the implementation here as similar as I can, | |
31 | //! changing only what was blatantly wrong, a typo, or sufficiently | |
32 | //! non-idiomatic rust that it really stuck out. | |
33 | //! | |
34 | //! In particular you'll see a certain amount of churn related to INTEGER vs. | |
35 | //! CARDINAL in the Mesa implementation. Mesa apparently interconverts the two | |
85aaf69f | 36 | //! somewhat readily? In any case, I've used usize for indices-in-buffers and |
1a4d82fc JJ |
37 | //! ints for character-sizes-and-indentation-offsets. This respects the need |
38 | //! for ints to "go negative" while carrying a pending-calculation balance, and | |
39 | //! helps differentiate all the numbers flying around internally (slightly). | |
40 | //! | |
41 | //! I also inverted the indentation arithmetic used in the print stack, since | |
42 | //! the Mesa implementation (somewhat randomly) stores the offset on the print | |
43 | //! stack in terms of margin-col rather than col itself. I store col. | |
44 | //! | |
45 | //! I also implemented a small change in the String token, in that I store an | |
46 | //! explicit length for the string. For most tokens this is just the length of | |
47 | //! the accompanying string. But it's necessary to permit it to differ, for | |
48 | //! encoding things that are supposed to "go on their own line" -- certain | |
49 | //! classes of comment and blank-line -- where relying on adjacent | |
50 | //! hardbreak-like Break tokens with long blankness indication doesn't actually | |
51 | //! work. To see why, consider when there is a "thing that should be on its own | |
52 | //! line" between two long blocks, say functions. If you put a hardbreak after | |
53 | //! each function (or before each) and the breaking algorithm decides to break | |
54 | //! there anyways (because the functions themselves are long) you wind up with | |
55 | //! extra blank lines. If you don't put hardbreaks you can wind up with the | |
56 | //! "thing which should be on its own line" not getting its own line in the | |
57 | //! rare case of "really small functions" or such. This re-occurs with comments | |
58 | //! and explicit blank lines. So in those cases we use a string with a payload | |
59 | //! we want isolated to a line and an explicit length that's huge, surrounded | |
60 | //! by two zero-length breaks. The algorithm will try its best to fit it on a | |
61 | //! line (which it can't) and so naturally place the content on its own line to | |
62 | //! avoid combining it with other lines and making matters even worse. | |
8bb4bdeb XL |
63 | //! |
64 | //! # Explanation | |
65 | //! | |
66 | //! In case you do not have the paper, here is an explanation of what's going | |
67 | //! on. | |
68 | //! | |
69 | //! There is a stream of input tokens flowing through this printer. | |
70 | //! | |
71 | //! The printer buffers up to 3N tokens inside itself, where N is linewidth. | |
72 | //! Yes, linewidth is chars and tokens are multi-char, but in the worst | |
73 | //! case every token worth buffering is 1 char long, so it's ok. | |
74 | //! | |
75 | //! Tokens are String, Break, and Begin/End to delimit blocks. | |
76 | //! | |
77 | //! Begin tokens can carry an offset, saying "how far to indent when you break | |
78 | //! inside here", as well as a flag indicating "consistent" or "inconsistent" | |
79 | //! breaking. Consistent breaking means that after the first break, no attempt | |
80 | //! will be made to flow subsequent breaks together onto lines. Inconsistent | |
81 | //! is the opposite. Inconsistent breaking example would be, say: | |
82 | //! | |
83 | //! ``` | |
84 | //! foo(hello, there, good, friends) | |
85 | //! ``` | |
86 | //! | |
87 | //! breaking inconsistently to become | |
88 | //! | |
89 | //! ``` | |
90 | //! foo(hello, there | |
91 | //! good, friends); | |
92 | //! ``` | |
93 | //! | |
94 | //! whereas a consistent breaking would yield: | |
95 | //! | |
96 | //! ``` | |
97 | //! foo(hello, | |
98 | //! there | |
99 | //! good, | |
100 | //! friends); | |
101 | //! ``` | |
102 | //! | |
103 | //! That is, in the consistent-break blocks we value vertical alignment | |
104 | //! more than the ability to cram stuff onto a line. But in all cases if it | |
105 | //! can make a block a one-liner, it'll do so. | |
106 | //! | |
107 | //! Carrying on with high-level logic: | |
108 | //! | |
109 | //! The buffered tokens go through a ring-buffer, 'tokens'. The 'left' and | |
110 | //! 'right' indices denote the active portion of the ring buffer as well as | |
111 | //! describing hypothetical points-in-the-infinite-stream at most 3N tokens | |
112 | //! apart (i.e. "not wrapped to ring-buffer boundaries"). The paper will switch | |
113 | //! between using 'left' and 'right' terms to denote the wrapped-to-ring-buffer | |
114 | //! and point-in-infinite-stream senses freely. | |
115 | //! | |
7cac9316 | 116 | //! There is a parallel ring buffer, `size`, that holds the calculated size of |
8bb4bdeb XL |
117 | //! each token. Why calculated? Because for Begin/End pairs, the "size" |
118 | //! includes everything between the pair. That is, the "size" of Begin is | |
119 | //! actually the sum of the sizes of everything between Begin and the paired | |
7cac9316 | 120 | //! End that follows. Since that is arbitrarily far in the future, `size` is |
8bb4bdeb | 121 | //! being rewritten regularly while the printer runs; in fact most of the |
7cac9316 | 122 | //! machinery is here to work out `size` entries on the fly (and give up when |
8bb4bdeb XL |
123 | //! they're so obviously over-long that "infinity" is a good enough |
124 | //! approximation for purposes of line breaking). | |
125 | //! | |
126 | //! The "input side" of the printer is managed as an abstract process called | |
7cac9316 | 127 | //! SCAN, which uses `scan_stack`, to manage calculating `size`. SCAN is, in |
8bb4bdeb XL |
128 | //! other words, the process of calculating 'size' entries. |
129 | //! | |
130 | //! The "output side" of the printer is managed by an abstract process called | |
7cac9316 | 131 | //! PRINT, which uses `print_stack`, `margin` and `space` to figure out what to |
8bb4bdeb XL |
132 | //! do with each token/size pair it consumes as it goes. It's trying to consume |
133 | //! the entire buffered window, but can't output anything until the size is >= | |
134 | //! 0 (sizes are set to negative while they're pending calculation). | |
135 | //! | |
136 | //! So SCAN takes input and buffers tokens and pending calculations, while | |
137 | //! PRINT gobbles up completed calculations and tokens from the buffer. The | |
138 | //! theory is that the two can never get more than 3N tokens apart, because | |
139 | //! once there's "obviously" too much data to fit on a line, in a size | |
140 | //! calculation, SCAN will write "infinity" to the size and let PRINT consume | |
141 | //! it. | |
142 | //! | |
143 | //! In this implementation (following the paper, again) the SCAN process is | |
144 | //! the method called `Printer::pretty_print`, and the 'PRINT' process is the method | |
145 | //! called `Printer::print`. | |
1a4d82fc | 146 | |
a7813a04 XL |
147 | use std::collections::VecDeque; |
148 | use std::fmt; | |
c34b1796 | 149 | use std::io; |
970d7e83 | 150 | |
8bb4bdeb | 151 | /// How to break. Described in more detail in the module docs. |
1a4d82fc JJ |
152 | #[derive(Clone, Copy, PartialEq)] |
153 | pub enum Breaks { | |
154 | Consistent, | |
155 | Inconsistent, | |
156 | } | |
223e47cc | 157 | |
1a4d82fc JJ |
158 | #[derive(Clone, Copy)] |
159 | pub struct BreakToken { | |
85aaf69f SL |
160 | offset: isize, |
161 | blank_space: isize | |
223e47cc LB |
162 | } |
163 | ||
1a4d82fc JJ |
164 | #[derive(Clone, Copy)] |
165 | pub struct BeginToken { | |
85aaf69f | 166 | offset: isize, |
1a4d82fc | 167 | breaks: Breaks |
223e47cc LB |
168 | } |
169 | ||
1a4d82fc JJ |
170 | #[derive(Clone)] |
171 | pub enum Token { | |
85aaf69f | 172 | String(String, isize), |
1a4d82fc JJ |
173 | Break(BreakToken), |
174 | Begin(BeginToken), | |
175 | End, | |
176 | Eof, | |
223e47cc LB |
177 | } |
178 | ||
1a4d82fc | 179 | impl Token { |
970d7e83 | 180 | pub fn is_eof(&self) -> bool { |
85aaf69f SL |
181 | match *self { |
182 | Token::Eof => true, | |
183 | _ => false, | |
184 | } | |
223e47cc | 185 | } |
970d7e83 LB |
186 | |
187 | pub fn is_hardbreak_tok(&self) -> bool { | |
223e47cc | 188 | match *self { |
85aaf69f | 189 | Token::Break(BreakToken { |
223e47cc LB |
190 | offset: 0, |
191 | blank_space: bs | |
1a4d82fc | 192 | }) if bs == SIZE_INFINITY => |
223e47cc LB |
193 | true, |
194 | _ => | |
195 | false | |
196 | } | |
197 | } | |
198 | } | |
199 | ||
a7813a04 XL |
200 | impl fmt::Display for Token { |
201 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
202 | match *self { | |
203 | Token::String(ref s, len) => write!(f, "STR({},{})", s, len), | |
204 | Token::Break(_) => f.write_str("BREAK"), | |
205 | Token::Begin(_) => f.write_str("BEGIN"), | |
206 | Token::End => f.write_str("END"), | |
207 | Token::Eof => f.write_str("EOF"), | |
208 | } | |
223e47cc LB |
209 | } |
210 | } | |
211 | ||
c30ab7b3 SL |
212 | fn buf_str(buf: &[BufEntry], left: usize, right: usize, lim: usize) -> String { |
213 | let n = buf.len(); | |
223e47cc | 214 | let mut i = left; |
1a4d82fc | 215 | let mut l = lim; |
a7813a04 | 216 | let mut s = String::from("["); |
85aaf69f SL |
217 | while i != right && l != 0 { |
218 | l -= 1; | |
970d7e83 LB |
219 | if i != left { |
220 | s.push_str(", "); | |
221 | } | |
c30ab7b3 | 222 | s.push_str(&format!("{}={}", buf[i].size, &buf[i].token)); |
85aaf69f | 223 | i += 1; |
223e47cc LB |
224 | i %= n; |
225 | } | |
1a4d82fc JJ |
226 | s.push(']'); |
227 | s | |
223e47cc LB |
228 | } |
229 | ||
c34b1796 | 230 | #[derive(Copy, Clone)] |
1a4d82fc JJ |
231 | pub enum PrintStackBreak { |
232 | Fits, | |
233 | Broken(Breaks), | |
234 | } | |
223e47cc | 235 | |
c34b1796 | 236 | #[derive(Copy, Clone)] |
1a4d82fc | 237 | pub struct PrintStackElem { |
85aaf69f | 238 | offset: isize, |
1a4d82fc | 239 | pbreak: PrintStackBreak |
223e47cc LB |
240 | } |
241 | ||
c34b1796 | 242 | const SIZE_INFINITY: isize = 0xffff; |
223e47cc | 243 | |
c34b1796 | 244 | pub fn mk_printer<'a>(out: Box<io::Write+'a>, linewidth: usize) -> Printer<'a> { |
c30ab7b3 | 245 | // Yes 55, it makes the ring buffers big enough to never fall behind. |
3157f602 | 246 | let n: usize = 55 * linewidth; |
1a4d82fc | 247 | debug!("mk_printer {}", linewidth); |
1a4d82fc JJ |
248 | Printer { |
249 | out: out, | |
223e47cc | 250 | buf_len: n, |
85aaf69f SL |
251 | margin: linewidth as isize, |
252 | space: linewidth as isize, | |
223e47cc LB |
253 | left: 0, |
254 | right: 0, | |
c30ab7b3 | 255 | buf: vec![BufEntry { token: Token::Eof, size: 0 }; n], |
223e47cc LB |
256 | left_total: 0, |
257 | right_total: 0, | |
c30ab7b3 | 258 | scan_stack: VecDeque::new(), |
1a4d82fc | 259 | print_stack: Vec::new(), |
223e47cc LB |
260 | pending_indentation: 0 |
261 | } | |
262 | } | |
263 | ||
c34b1796 | 264 | pub struct Printer<'a> { |
041b39d2 | 265 | out: Box<io::Write+'a>, |
85aaf69f | 266 | buf_len: usize, |
1a4d82fc | 267 | /// Width of lines we're constrained to |
85aaf69f | 268 | margin: isize, |
1a4d82fc | 269 | /// Number of spaces left on line |
85aaf69f | 270 | space: isize, |
1a4d82fc | 271 | /// Index of left side of input stream |
85aaf69f | 272 | left: usize, |
1a4d82fc | 273 | /// Index of right side of input stream |
85aaf69f | 274 | right: usize, |
c30ab7b3 SL |
275 | /// Ring-buffer of tokens and calculated sizes |
276 | buf: Vec<BufEntry>, | |
1a4d82fc | 277 | /// Running size of stream "...left" |
85aaf69f | 278 | left_total: isize, |
1a4d82fc | 279 | /// Running size of stream "...right" |
85aaf69f | 280 | right_total: isize, |
1a4d82fc JJ |
281 | /// Pseudo-stack, really a ring too. Holds the |
282 | /// primary-ring-buffers index of the Begin that started the | |
283 | /// current block, possibly with the most recent Break after that | |
284 | /// Begin (if there is any) on top of it. Stuff is flushed off the | |
285 | /// bottom as it becomes irrelevant due to the primary ring-buffer | |
286 | /// advancing. | |
c30ab7b3 | 287 | scan_stack: VecDeque<usize>, |
1a4d82fc JJ |
288 | /// Stack of blocks-in-progress being flushed by print |
289 | print_stack: Vec<PrintStackElem> , | |
290 | /// Buffered indentation to avoid writing trailing whitespace | |
85aaf69f | 291 | pending_indentation: isize, |
223e47cc LB |
292 | } |
293 | ||
c30ab7b3 SL |
294 | #[derive(Clone)] |
295 | struct BufEntry { | |
296 | token: Token, | |
297 | size: isize, | |
298 | } | |
299 | ||
c34b1796 | 300 | impl<'a> Printer<'a> { |
1a4d82fc | 301 | pub fn last_token(&mut self) -> Token { |
c30ab7b3 | 302 | self.buf[self.right].token.clone() |
1a4d82fc | 303 | } |
8bb4bdeb | 304 | /// be very careful with this! |
1a4d82fc | 305 | pub fn replace_last_token(&mut self, t: Token) { |
c30ab7b3 | 306 | self.buf[self.right].token = t; |
970d7e83 | 307 | } |
c34b1796 | 308 | pub fn pretty_print(&mut self, token: Token) -> io::Result<()> { |
d9579d0f | 309 | debug!("pp Vec<{},{}>", self.left, self.right); |
85aaf69f SL |
310 | match token { |
311 | Token::Eof => { | |
a7813a04 | 312 | if !self.scan_stack.is_empty() { |
223e47cc | 313 | self.check_stack(0); |
54a0048b | 314 | self.advance_left()?; |
223e47cc LB |
315 | } |
316 | self.indent(0); | |
1a4d82fc | 317 | Ok(()) |
223e47cc | 318 | } |
85aaf69f | 319 | Token::Begin(b) => { |
a7813a04 | 320 | if self.scan_stack.is_empty() { |
223e47cc LB |
321 | self.left_total = 1; |
322 | self.right_total = 1; | |
85aaf69f SL |
323 | self.left = 0; |
324 | self.right = 0; | |
223e47cc | 325 | } else { self.advance_right(); } |
d9579d0f | 326 | debug!("pp Begin({})/buffer Vec<{},{}>", |
223e47cc | 327 | b.offset, self.left, self.right); |
c30ab7b3 | 328 | self.buf[self.right] = BufEntry { token: token, size: -self.right_total }; |
1a4d82fc JJ |
329 | let right = self.right; |
330 | self.scan_push(right); | |
331 | Ok(()) | |
223e47cc | 332 | } |
85aaf69f | 333 | Token::End => { |
a7813a04 | 334 | if self.scan_stack.is_empty() { |
d9579d0f | 335 | debug!("pp End/print Vec<{},{}>", self.left, self.right); |
85aaf69f | 336 | self.print(token, 0) |
223e47cc | 337 | } else { |
d9579d0f | 338 | debug!("pp End/buffer Vec<{},{}>", self.left, self.right); |
223e47cc | 339 | self.advance_right(); |
c30ab7b3 | 340 | self.buf[self.right] = BufEntry { token: token, size: -1 }; |
1a4d82fc JJ |
341 | let right = self.right; |
342 | self.scan_push(right); | |
343 | Ok(()) | |
223e47cc LB |
344 | } |
345 | } | |
85aaf69f | 346 | Token::Break(b) => { |
a7813a04 | 347 | if self.scan_stack.is_empty() { |
223e47cc LB |
348 | self.left_total = 1; |
349 | self.right_total = 1; | |
85aaf69f SL |
350 | self.left = 0; |
351 | self.right = 0; | |
223e47cc | 352 | } else { self.advance_right(); } |
d9579d0f | 353 | debug!("pp Break({})/buffer Vec<{},{}>", |
223e47cc LB |
354 | b.offset, self.left, self.right); |
355 | self.check_stack(0); | |
1a4d82fc JJ |
356 | let right = self.right; |
357 | self.scan_push(right); | |
c30ab7b3 | 358 | self.buf[self.right] = BufEntry { token: token, size: -self.right_total }; |
223e47cc | 359 | self.right_total += b.blank_space; |
1a4d82fc | 360 | Ok(()) |
223e47cc | 361 | } |
85aaf69f | 362 | Token::String(s, len) => { |
a7813a04 | 363 | if self.scan_stack.is_empty() { |
d9579d0f | 364 | debug!("pp String('{}')/print Vec<{},{}>", |
85aaf69f SL |
365 | s, self.left, self.right); |
366 | self.print(Token::String(s, len), len) | |
223e47cc | 367 | } else { |
d9579d0f | 368 | debug!("pp String('{}')/buffer Vec<{},{}>", |
85aaf69f | 369 | s, self.left, self.right); |
223e47cc | 370 | self.advance_right(); |
c30ab7b3 | 371 | self.buf[self.right] = BufEntry { token: Token::String(s, len), size: len }; |
223e47cc | 372 | self.right_total += len; |
1a4d82fc | 373 | self.check_stream() |
223e47cc LB |
374 | } |
375 | } | |
376 | } | |
377 | } | |
c34b1796 | 378 | pub fn check_stream(&mut self) -> io::Result<()> { |
d9579d0f | 379 | debug!("check_stream Vec<{}, {}> with left_total={}, right_total={}", |
223e47cc LB |
380 | self.left, self.right, self.left_total, self.right_total); |
381 | if self.right_total - self.left_total > self.space { | |
1a4d82fc | 382 | debug!("scan window is {}, longer than space on line ({})", |
223e47cc | 383 | self.right_total - self.left_total, self.space); |
a7813a04 XL |
384 | if Some(&self.left) == self.scan_stack.back() { |
385 | debug!("setting {} to infinity and popping", self.left); | |
386 | let scanned = self.scan_pop_bottom(); | |
c30ab7b3 | 387 | self.buf[scanned].size = SIZE_INFINITY; |
223e47cc | 388 | } |
54a0048b | 389 | self.advance_left()?; |
1a4d82fc | 390 | if self.left != self.right { |
54a0048b | 391 | self.check_stream()?; |
1a4d82fc | 392 | } |
223e47cc | 393 | } |
1a4d82fc | 394 | Ok(()) |
223e47cc | 395 | } |
85aaf69f | 396 | pub fn scan_push(&mut self, x: usize) { |
1a4d82fc | 397 | debug!("scan_push {}", x); |
a7813a04 | 398 | self.scan_stack.push_front(x); |
223e47cc | 399 | } |
85aaf69f | 400 | pub fn scan_pop(&mut self) -> usize { |
a7813a04 | 401 | self.scan_stack.pop_front().unwrap() |
223e47cc | 402 | } |
85aaf69f | 403 | pub fn scan_top(&mut self) -> usize { |
a7813a04 | 404 | *self.scan_stack.front().unwrap() |
223e47cc | 405 | } |
85aaf69f | 406 | pub fn scan_pop_bottom(&mut self) -> usize { |
a7813a04 | 407 | self.scan_stack.pop_back().unwrap() |
223e47cc | 408 | } |
970d7e83 | 409 | pub fn advance_right(&mut self) { |
85aaf69f | 410 | self.right += 1; |
223e47cc | 411 | self.right %= self.buf_len; |
7cac9316 | 412 | assert_ne!(self.right, self.left); |
223e47cc | 413 | } |
c34b1796 | 414 | pub fn advance_left(&mut self) -> io::Result<()> { |
d9579d0f | 415 | debug!("advance_left Vec<{},{}>, sizeof({})={}", self.left, self.right, |
c30ab7b3 | 416 | self.left, self.buf[self.left].size); |
85aaf69f | 417 | |
c30ab7b3 | 418 | let mut left_size = self.buf[self.left].size; |
85aaf69f SL |
419 | |
420 | while left_size >= 0 { | |
c30ab7b3 | 421 | let left = self.buf[self.left].token.clone(); |
85aaf69f SL |
422 | |
423 | let len = match left { | |
424 | Token::Break(b) => b.blank_space, | |
425 | Token::String(_, len) => { | |
426 | assert_eq!(len, left_size); | |
427 | len | |
428 | } | |
429 | _ => 0 | |
430 | }; | |
431 | ||
54a0048b | 432 | self.print(left, left_size)?; |
85aaf69f SL |
433 | |
434 | self.left_total += len; | |
435 | ||
436 | if self.left == self.right { | |
437 | break; | |
223e47cc | 438 | } |
85aaf69f SL |
439 | |
440 | self.left += 1; | |
441 | self.left %= self.buf_len; | |
442 | ||
c30ab7b3 | 443 | left_size = self.buf[self.left].size; |
223e47cc | 444 | } |
85aaf69f SL |
445 | |
446 | Ok(()) | |
223e47cc | 447 | } |
85aaf69f | 448 | pub fn check_stack(&mut self, k: isize) { |
a7813a04 | 449 | if !self.scan_stack.is_empty() { |
223e47cc | 450 | let x = self.scan_top(); |
c30ab7b3 | 451 | match self.buf[x].token { |
85aaf69f | 452 | Token::Begin(_) => { |
1a4d82fc JJ |
453 | if k > 0 { |
454 | let popped = self.scan_pop(); | |
c30ab7b3 | 455 | self.buf[popped].size = self.buf[x].size + self.right_total; |
1a4d82fc JJ |
456 | self.check_stack(k - 1); |
457 | } | |
458 | } | |
85aaf69f | 459 | Token::End => { |
1a4d82fc JJ |
460 | // paper says + not =, but that makes no sense. |
461 | let popped = self.scan_pop(); | |
c30ab7b3 | 462 | self.buf[popped].size = 1; |
1a4d82fc JJ |
463 | self.check_stack(k + 1); |
464 | } | |
465 | _ => { | |
466 | let popped = self.scan_pop(); | |
c30ab7b3 | 467 | self.buf[popped].size = self.buf[x].size + self.right_total; |
1a4d82fc JJ |
468 | if k > 0 { |
469 | self.check_stack(k); | |
470 | } | |
223e47cc | 471 | } |
223e47cc LB |
472 | } |
473 | } | |
474 | } | |
c34b1796 | 475 | pub fn print_newline(&mut self, amount: isize) -> io::Result<()> { |
1a4d82fc JJ |
476 | debug!("NEWLINE {}", amount); |
477 | let ret = write!(self.out, "\n"); | |
223e47cc LB |
478 | self.pending_indentation = 0; |
479 | self.indent(amount); | |
a7813a04 | 480 | ret |
223e47cc | 481 | } |
85aaf69f | 482 | pub fn indent(&mut self, amount: isize) { |
1a4d82fc | 483 | debug!("INDENT {}", amount); |
223e47cc LB |
484 | self.pending_indentation += amount; |
485 | } | |
1a4d82fc | 486 | pub fn get_top(&mut self) -> PrintStackElem { |
a7813a04 XL |
487 | match self.print_stack.last() { |
488 | Some(el) => *el, | |
489 | None => PrintStackElem { | |
223e47cc | 490 | offset: 0, |
85aaf69f | 491 | pbreak: PrintStackBreak::Broken(Breaks::Inconsistent) |
223e47cc LB |
492 | } |
493 | } | |
494 | } | |
c34b1796 | 495 | pub fn print_str(&mut self, s: &str) -> io::Result<()> { |
223e47cc | 496 | while self.pending_indentation > 0 { |
54a0048b | 497 | write!(self.out, " ")?; |
223e47cc LB |
498 | self.pending_indentation -= 1; |
499 | } | |
1a4d82fc | 500 | write!(self.out, "{}", s) |
223e47cc | 501 | } |
c34b1796 | 502 | pub fn print(&mut self, token: Token, l: isize) -> io::Result<()> { |
a7813a04 | 503 | debug!("print {} {} (remaining line space={})", token, l, |
223e47cc | 504 | self.space); |
c30ab7b3 | 505 | debug!("{}", buf_str(&self.buf, |
1a4d82fc JJ |
506 | self.left, |
507 | self.right, | |
508 | 6)); | |
85aaf69f SL |
509 | match token { |
510 | Token::Begin(b) => { | |
1a4d82fc | 511 | if l > self.space { |
223e47cc | 512 | let col = self.margin - self.space + b.offset; |
1a4d82fc JJ |
513 | debug!("print Begin -> push broken block at col {}", col); |
514 | self.print_stack.push(PrintStackElem { | |
223e47cc | 515 | offset: col, |
85aaf69f | 516 | pbreak: PrintStackBreak::Broken(b.breaks) |
223e47cc LB |
517 | }); |
518 | } else { | |
1a4d82fc JJ |
519 | debug!("print Begin -> push fitting block"); |
520 | self.print_stack.push(PrintStackElem { | |
223e47cc | 521 | offset: 0, |
85aaf69f | 522 | pbreak: PrintStackBreak::Fits |
223e47cc LB |
523 | }); |
524 | } | |
1a4d82fc | 525 | Ok(()) |
223e47cc | 526 | } |
85aaf69f | 527 | Token::End => { |
1a4d82fc JJ |
528 | debug!("print End -> pop End"); |
529 | let print_stack = &mut self.print_stack; | |
a7813a04 | 530 | assert!(!print_stack.is_empty()); |
1a4d82fc JJ |
531 | print_stack.pop().unwrap(); |
532 | Ok(()) | |
223e47cc | 533 | } |
85aaf69f | 534 | Token::Break(b) => { |
223e47cc LB |
535 | let top = self.get_top(); |
536 | match top.pbreak { | |
85aaf69f | 537 | PrintStackBreak::Fits => { |
1a4d82fc | 538 | debug!("print Break({}) in fitting block", b.blank_space); |
223e47cc LB |
539 | self.space -= b.blank_space; |
540 | self.indent(b.blank_space); | |
1a4d82fc | 541 | Ok(()) |
223e47cc | 542 | } |
85aaf69f | 543 | PrintStackBreak::Broken(Breaks::Consistent) => { |
1a4d82fc | 544 | debug!("print Break({}+{}) in consistent block", |
223e47cc | 545 | top.offset, b.offset); |
1a4d82fc | 546 | let ret = self.print_newline(top.offset + b.offset); |
223e47cc | 547 | self.space = self.margin - (top.offset + b.offset); |
1a4d82fc | 548 | ret |
223e47cc | 549 | } |
85aaf69f | 550 | PrintStackBreak::Broken(Breaks::Inconsistent) => { |
1a4d82fc JJ |
551 | if l > self.space { |
552 | debug!("print Break({}+{}) w/ newline in inconsistent", | |
223e47cc | 553 | top.offset, b.offset); |
1a4d82fc | 554 | let ret = self.print_newline(top.offset + b.offset); |
223e47cc | 555 | self.space = self.margin - (top.offset + b.offset); |
1a4d82fc | 556 | ret |
223e47cc | 557 | } else { |
1a4d82fc | 558 | debug!("print Break({}) w/o newline in inconsistent", |
223e47cc LB |
559 | b.blank_space); |
560 | self.indent(b.blank_space); | |
561 | self.space -= b.blank_space; | |
1a4d82fc | 562 | Ok(()) |
223e47cc LB |
563 | } |
564 | } | |
565 | } | |
566 | } | |
a7813a04 | 567 | Token::String(ref s, len) => { |
1a4d82fc JJ |
568 | debug!("print String({})", s); |
569 | assert_eq!(l, len); | |
570 | // assert!(l <= space); | |
223e47cc | 571 | self.space -= len; |
a7813a04 | 572 | self.print_str(s) |
223e47cc | 573 | } |
85aaf69f | 574 | Token::Eof => { |
1a4d82fc JJ |
575 | // Eof should never get here. |
576 | panic!(); | |
223e47cc LB |
577 | } |
578 | } | |
579 | } | |
223e47cc | 580 | |
041b39d2 | 581 | // Convenience functions to talk to the printer. |
8bb4bdeb | 582 | |
041b39d2 XL |
583 | /// "raw box" |
584 | pub fn rbox(&mut self, indent: usize, b: Breaks) -> io::Result<()> { | |
585 | self.pretty_print(Token::Begin(BeginToken { | |
586 | offset: indent as isize, | |
587 | breaks: b | |
588 | })) | |
589 | } | |
223e47cc | 590 | |
041b39d2 XL |
591 | /// Inconsistent breaking box |
592 | pub fn ibox(&mut self, indent: usize) -> io::Result<()> { | |
593 | self.rbox(indent, Breaks::Inconsistent) | |
594 | } | |
223e47cc | 595 | |
041b39d2 XL |
596 | /// Consistent breaking box |
597 | pub fn cbox(&mut self, indent: usize) -> io::Result<()> { | |
598 | self.rbox(indent, Breaks::Consistent) | |
599 | } | |
223e47cc | 600 | |
041b39d2 XL |
601 | pub fn break_offset(&mut self, n: usize, off: isize) -> io::Result<()> { |
602 | self.pretty_print(Token::Break(BreakToken { | |
603 | offset: off, | |
604 | blank_space: n as isize | |
605 | })) | |
606 | } | |
223e47cc | 607 | |
041b39d2 XL |
608 | pub fn end(&mut self) -> io::Result<()> { |
609 | self.pretty_print(Token::End) | |
610 | } | |
223e47cc | 611 | |
041b39d2 XL |
612 | pub fn eof(&mut self) -> io::Result<()> { |
613 | self.pretty_print(Token::Eof) | |
614 | } | |
223e47cc | 615 | |
041b39d2 XL |
616 | pub fn word(&mut self, wrd: &str) -> io::Result<()> { |
617 | self.pretty_print(Token::String(wrd.to_string(), wrd.len() as isize)) | |
618 | } | |
223e47cc | 619 | |
041b39d2 XL |
620 | pub fn huge_word(&mut self, wrd: &str) -> io::Result<()> { |
621 | self.pretty_print(Token::String(wrd.to_string(), SIZE_INFINITY)) | |
622 | } | |
223e47cc | 623 | |
041b39d2 XL |
624 | pub fn zero_word(&mut self, wrd: &str) -> io::Result<()> { |
625 | self.pretty_print(Token::String(wrd.to_string(), 0)) | |
626 | } | |
223e47cc | 627 | |
041b39d2 XL |
628 | fn spaces(&mut self, n: usize) -> io::Result<()> { |
629 | self.break_offset(n, 0) | |
630 | } | |
223e47cc | 631 | |
041b39d2 XL |
632 | pub fn zerobreak(&mut self) -> io::Result<()> { |
633 | self.spaces(0) | |
634 | } | |
223e47cc | 635 | |
041b39d2 XL |
636 | pub fn space(&mut self) -> io::Result<()> { |
637 | self.spaces(1) | |
638 | } | |
223e47cc | 639 | |
041b39d2 XL |
640 | pub fn hardbreak(&mut self) -> io::Result<()> { |
641 | self.spaces(SIZE_INFINITY as usize) | |
642 | } | |
223e47cc | 643 | |
041b39d2 XL |
644 | pub fn hardbreak_tok_offset(off: isize) -> Token { |
645 | Token::Break(BreakToken {offset: off, blank_space: SIZE_INFINITY}) | |
646 | } | |
223e47cc | 647 | |
041b39d2 XL |
648 | pub fn hardbreak_tok() -> Token { |
649 | Self::hardbreak_tok_offset(0) | |
650 | } | |
85aaf69f | 651 | } |