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