1use std::cmp::max;
9
10use super::*;
11
12use log::debug;
13use ress::tokens::*;
14use ress::*;
15
16#[derive(Clone)]
17struct Tok<'a> {
18 token: Token<&'a str>,
19 start: SourceCoord,
20 end: SourceCoord,
21 starts_array_literal: bool,
22}
23
24type Stack<'a> = Vec<Tok<'a>>;
25
26fn top_token<'a, 'b>(s: &'b Stack<'a>) -> Option<&'b Token<&'a str>> {
27 s.last().map(|v| &v.token)
28}
29
30fn is_pre_array_literal_token(token: &Tok) -> bool {
31 match &token.token {
32 &Token::Keyword(Keyword::Case(_))
33 | &Token::Keyword(Keyword::Delete(_))
34 | &Token::Keyword(Keyword::Do(_))
35 | &Token::Keyword(Keyword::Else(_))
36 | &Token::Keyword(Keyword::In(_))
37 | &Token::Keyword(Keyword::InstanceOf(_))
38 | &Token::Keyword(Keyword::TypeOf(_))
39 | &Token::Keyword(Keyword::Void(_))
40 | &Token::Punct(Punct::Ampersand)
41 | &Token::Punct(Punct::AmpersandEqual)
42 | &Token::Punct(Punct::Asterisk)
43 | &Token::Punct(Punct::AsteriskEqual)
44 | &Token::Punct(Punct::Bang)
45 | &Token::Punct(Punct::BangDoubleEqual)
46 | &Token::Punct(Punct::BangEqual)
47 | &Token::Punct(Punct::Caret)
48 | &Token::Punct(Punct::CaretEqual)
49 | &Token::Punct(Punct::CloseBrace)
50 | &Token::Punct(Punct::Colon)
51 | &Token::Punct(Punct::Comma)
52 | &Token::Punct(Punct::Dash)
53 | &Token::Punct(Punct::DashEqual)
54 | &Token::Punct(Punct::DoubleAmpersand)
55 | &Token::Punct(Punct::DoubleAsterisk)
56 | &Token::Punct(Punct::DoubleAsteriskEqual)
57 | &Token::Punct(Punct::DoubleDash)
58 | &Token::Punct(Punct::DoubleEqual)
59 | &Token::Punct(Punct::DoubleGreaterThan)
60 | &Token::Punct(Punct::DoubleGreaterThanEqual)
61 | &Token::Punct(Punct::DoubleLessThan)
62 | &Token::Punct(Punct::DoubleLessThanEqual)
63 | &Token::Punct(Punct::DoublePipe)
64 | &Token::Punct(Punct::DoublePlus)
65 | &Token::Punct(Punct::Equal)
66 | &Token::Punct(Punct::ForwardSlash)
67 | &Token::Punct(Punct::ForwardSlashEqual)
68 | &Token::Punct(Punct::GreaterThan)
69 | &Token::Punct(Punct::GreaterThanEqual)
70 | &Token::Punct(Punct::LessThan)
71 | &Token::Punct(Punct::LessThanEqual)
72 | &Token::Punct(Punct::OpenBrace)
73 | &Token::Punct(Punct::Percent)
74 | &Token::Punct(Punct::PercentEqual)
75 | &Token::Punct(Punct::Pipe)
76 | &Token::Punct(Punct::PipeEqual)
77 | &Token::Punct(Punct::Plus)
78 | &Token::Punct(Punct::PlusEqual)
79 | &Token::Punct(Punct::QuestionMark)
80 | &Token::Punct(Punct::Tilde)
81 | &Token::Punct(Punct::TripleEqual)
82 | &Token::Punct(Punct::TripleGreaterThan)
83 | &Token::Punct(Punct::TripleGreaterThanEqual)
84 | &Token::Punct(Punct::SemiColon) => true,
85 _ => false,
86 }
87}
88
89fn starts_array_literal<'a>(token: &Tok, last_token: &Option<Tok>) -> bool {
90 if &token.token != &Token::Punct(Punct::OpenBracket) {
91 return false;
92 }
93 if let Some(ref t) = last_token {
94 is_pre_array_literal_token(t)
95 } else {
96 true
97 }
98}
99
100fn prevent_asi_after_token(token: &Tok) -> bool {
101 match &token.token {
102 &Token::Keyword(Keyword::Delete(_))
103 | &Token::Keyword(Keyword::In(_))
104 | &Token::Keyword(Keyword::InstanceOf(_))
105 | &Token::Keyword(Keyword::TypeOf(_))
106 | &Token::Keyword(Keyword::Void(_))
107 | &Token::Keyword(Keyword::New(_))
108 | &Token::Punct(Punct::Ampersand)
109 | &Token::Punct(Punct::AmpersandEqual)
110 | &Token::Punct(Punct::Asterisk)
111 | &Token::Punct(Punct::AsteriskEqual)
112 | &Token::Punct(Punct::Bang)
113 | &Token::Punct(Punct::BangDoubleEqual)
114 | &Token::Punct(Punct::BangEqual)
115 | &Token::Punct(Punct::Caret)
116 | &Token::Punct(Punct::CaretEqual)
117 | &Token::Punct(Punct::Comma)
118 | &Token::Punct(Punct::Dash)
119 | &Token::Punct(Punct::DashEqual)
120 | &Token::Punct(Punct::DoubleAmpersand)
121 | &Token::Punct(Punct::DoubleAsterisk)
122 | &Token::Punct(Punct::DoubleAsteriskEqual)
123 | &Token::Punct(Punct::DoubleGreaterThan)
124 | &Token::Punct(Punct::DoubleGreaterThanEqual)
125 | &Token::Punct(Punct::DoubleLessThan)
126 | &Token::Punct(Punct::DoubleLessThanEqual)
127 | &Token::Punct(Punct::DoublePipe)
128 | &Token::Punct(Punct::Equal)
129 | &Token::Punct(Punct::ForwardSlash)
130 | &Token::Punct(Punct::ForwardSlashEqual)
131 | &Token::Punct(Punct::GreaterThan)
132 | &Token::Punct(Punct::GreaterThanEqual)
133 | &Token::Punct(Punct::LessThan)
134 | &Token::Punct(Punct::LessThanEqual)
135 | &Token::Punct(Punct::OpenParen)
136 | &Token::Punct(Punct::Percent)
137 | &Token::Punct(Punct::PercentEqual)
138 | &Token::Punct(Punct::Period)
139 | &Token::Punct(Punct::Pipe)
140 | &Token::Punct(Punct::PipeEqual)
141 | &Token::Punct(Punct::Plus)
142 | &Token::Punct(Punct::PlusEqual)
143 | &Token::Punct(Punct::Tilde)
144 | &Token::Punct(Punct::TripleEqual)
145 | &Token::Punct(Punct::TripleGreaterThan)
146 | &Token::Punct(Punct::TripleGreaterThanEqual) => true,
147 _ => false,
148 }
149}
150
151fn prevent_asi_before_token(token: &Tok) -> bool {
152 match &token.token {
153 &Token::Keyword(Keyword::In(_))
154 | &Token::Keyword(Keyword::InstanceOf(_))
155 | &Token::Punct(Punct::Ampersand)
156 | &Token::Punct(Punct::AmpersandEqual)
157 | &Token::Punct(Punct::Asterisk)
158 | &Token::Punct(Punct::AsteriskEqual)
159 | &Token::Punct(Punct::Bang)
160 | &Token::Punct(Punct::BangDoubleEqual)
161 | &Token::Punct(Punct::BangEqual)
162 | &Token::Punct(Punct::Caret)
163 | &Token::Punct(Punct::CaretEqual)
164 | &Token::Punct(Punct::Comma)
165 | &Token::Punct(Punct::Dash)
166 | &Token::Punct(Punct::DashEqual)
167 | &Token::Punct(Punct::DoubleAmpersand)
168 | &Token::Punct(Punct::DoubleAsterisk)
169 | &Token::Punct(Punct::DoubleAsteriskEqual)
170 | &Token::Punct(Punct::DoubleGreaterThan)
171 | &Token::Punct(Punct::DoubleGreaterThanEqual)
172 | &Token::Punct(Punct::DoubleLessThan)
173 | &Token::Punct(Punct::DoubleLessThanEqual)
174 | &Token::Punct(Punct::DoublePipe)
175 | &Token::Punct(Punct::Equal)
176 | &Token::Punct(Punct::ForwardSlash)
177 | &Token::Punct(Punct::ForwardSlashEqual)
178 | &Token::Punct(Punct::GreaterThan)
179 | &Token::Punct(Punct::GreaterThanEqual)
180 | &Token::Punct(Punct::LessThan)
181 | &Token::Punct(Punct::LessThanEqual)
182 | &Token::Punct(Punct::OpenParen)
183 | &Token::Punct(Punct::Percent)
184 | &Token::Punct(Punct::PercentEqual)
185 | &Token::Punct(Punct::Period)
186 | &Token::Punct(Punct::Pipe)
187 | &Token::Punct(Punct::PipeEqual)
188 | &Token::Punct(Punct::Plus)
189 | &Token::Punct(Punct::PlusEqual)
190 | &Token::Punct(Punct::Tilde)
191 | &Token::Punct(Punct::TripleEqual)
192 | &Token::Punct(Punct::TripleGreaterThan)
193 | &Token::Punct(Punct::TripleGreaterThanEqual) => true,
194 _ => false,
195 }
196}
197
198fn is_identifier_like(token: &Tok) -> bool {
199 match &token.token {
200 &Token::Boolean(_)
201 | &Token::Ident(_)
202 | &Token::Keyword(_)
203 | &Token::Null
204 | &Token::Number(_) => true,
205 _ => false,
206 }
207}
208
209fn is_asi(token: &Tok, last_token: &Option<Tok>) -> bool {
210 let t = if let Some(ref t) = last_token {
211 t
212 } else {
213 return false;
214 };
215 if token.start.line == t.start.line {
216 return false;
217 }
218 match &t.token {
219 &Token::Keyword(Keyword::Return(_)) | &Token::Keyword(Keyword::Yield(_)) => return true,
220 _ => (),
221 }
222 if prevent_asi_after_token(t) || prevent_asi_before_token(token) {
223 return false;
224 }
225 true
226}
227
228fn is_line_delimiter(token: &Tok, stack: &Stack) -> bool {
229 if token.starts_array_literal {
230 return true;
231 }
232 match &token.token {
233 &Token::Punct(Punct::SemiColon) | &Token::Punct(Punct::Comma) => {
234 top_token(stack) != Some(&Token::Punct(Punct::OpenParen))
235 }
236 &Token::Punct(Punct::OpenBrace) => true,
237 &Token::Punct(Punct::Colon) => match stack.last().map(|v| &v.token) {
238 Some(&Token::Keyword(Keyword::Case(_)))
239 | Some(&Token::Keyword(Keyword::Default(_))) => true,
240 _ => false,
241 },
242 _ => false,
243 }
244}
245
246struct Writer {
247 buffer: String,
248 current: SourceCoord,
249 last_from: SourceCoord,
250 mappings: Vec<SourceMapping>,
251 indent: u32,
252}
253
254impl Writer {
255 fn new(indent: u32) -> Writer {
256 Writer {
257 buffer: String::new(),
258 current: SourceCoord {
259 line: SourceMapLine(0),
260 column: SourceMapColumn(0),
261 },
262 last_from: SourceCoord {
263 line: SourceMapLine(0),
264 column: SourceMapColumn(0),
265 },
266 mappings: Vec::new(),
267 indent: indent,
268 }
269 }
270 fn write_new(&mut self, s: &str) {
271 if self.mappings.is_empty() {
272 self.mappings.push(SourceMapping {
273 from: self.current,
274 to: self.last_from,
275 });
276 }
277 self.update_current(s);
278 }
279 fn write(&mut self, s: &str, from: SourceCoord) {
280 self.last_from = from;
281 self.mappings.push(SourceMapping {
282 from: self.current,
283 to: self.last_from,
284 });
285 self.update_current(s);
286 }
287 fn write_indent(&mut self, level: u32, from: SourceCoord) {
288 if level == 0 {
289 return;
290 }
291 self.last_from = from;
292 self.mappings.push(SourceMapping {
293 from: self.current,
294 to: self.last_from,
295 });
296 let count = level * self.indent;
297 for _ in 0..count {
298 self.buffer.push(' ');
299 }
300 self.current.column.0 += count;
301 }
302 fn update_current(&mut self, s: &str) {
303 self.buffer.push_str(s);
304 for ch in s.chars() {
305 if ch == '\n' {
306 self.current.line.0 += 1;
307 self.current.column.0 = 0;
308 } else {
309 self.current.column.0 += ch.len_utf16() as u32;
310 }
311 }
312 }
313}
314
315fn append_newline(token: &Tok, stack: &Stack, out: &mut Writer) -> bool {
316 if is_line_delimiter(token, stack) {
317 out.write("\n", token.start);
318 return true;
319 }
320 false
321}
322
323fn need_space_after(token: &Tok, last_token: &Option<Tok>) -> bool {
324 if let Some(t) = last_token.as_ref() {
325 match &t.token {
326 &Token::Keyword(Keyword::Do(_))
327 | &Token::Keyword(Keyword::For(_))
328 | &Token::Keyword(Keyword::While(_))
329 | &Token::Punct(Punct::Ampersand)
330 | &Token::Punct(Punct::AmpersandEqual)
331 | &Token::Punct(Punct::Asterisk)
332 | &Token::Punct(Punct::AsteriskEqual)
333 | &Token::Punct(Punct::BangDoubleEqual)
334 | &Token::Punct(Punct::BangEqual)
335 | &Token::Punct(Punct::Caret)
336 | &Token::Punct(Punct::CaretEqual)
337 | &Token::Punct(Punct::Colon)
338 | &Token::Punct(Punct::Comma)
339 | &Token::Punct(Punct::Dash)
340 | &Token::Punct(Punct::DashEqual)
341 | &Token::Punct(Punct::DoubleAmpersand)
342 | &Token::Punct(Punct::DoubleAsterisk)
343 | &Token::Punct(Punct::DoubleAsteriskEqual)
344 | &Token::Punct(Punct::DoubleEqual)
345 | &Token::Punct(Punct::DoubleGreaterThan)
346 | &Token::Punct(Punct::DoubleGreaterThanEqual)
347 | &Token::Punct(Punct::DoubleLessThan)
348 | &Token::Punct(Punct::DoubleLessThanEqual)
349 | &Token::Punct(Punct::DoublePipe)
350 | &Token::Punct(Punct::Equal)
351 | &Token::Punct(Punct::ForwardSlash)
352 | &Token::Punct(Punct::ForwardSlashEqual)
353 | &Token::Punct(Punct::GreaterThan)
354 | &Token::Punct(Punct::GreaterThanEqual)
355 | &Token::Punct(Punct::LessThan)
356 | &Token::Punct(Punct::LessThanEqual)
357 | &Token::Punct(Punct::Percent)
358 | &Token::Punct(Punct::PercentEqual)
359 | &Token::Punct(Punct::Pipe)
360 | &Token::Punct(Punct::PipeEqual)
361 | &Token::Punct(Punct::Plus)
362 | &Token::Punct(Punct::PlusEqual)
363 | &Token::Punct(Punct::QuestionMark)
364 | &Token::Punct(Punct::SemiColon)
365 | &Token::Punct(Punct::TripleEqual)
366 | &Token::Punct(Punct::TripleGreaterThan)
367 | &Token::Punct(Punct::TripleGreaterThanEqual) => return true,
368 &Token::Number(_) => {
369 if let &Token::Punct(Punct::Period) = &token.token {
370 return true;
371 }
372 }
373 &Token::Keyword(Keyword::Break(_))
374 | &Token::Keyword(Keyword::Continue(_))
375 | &Token::Keyword(Keyword::Return(_)) => match &token.token {
376 &Token::Punct(Punct::Period) | &Token::Punct(Punct::SemiColon) => (),
377 _ => return true,
378 },
379 &Token::Keyword(Keyword::Debugger(_))
380 | &Token::Keyword(Keyword::Default(_))
381 | &Token::Keyword(Keyword::This(_)) => (),
382 &Token::Keyword(_) => match &token.token {
383 &Token::Punct(Punct::Period) => (),
384 _ => return true,
385 },
386 &Token::Punct(Punct::CloseParen) => match &token.token {
387 &Token::Punct(Punct::CloseParen)
388 | &Token::Punct(Punct::CloseBracket)
389 | &Token::Punct(Punct::SemiColon)
390 | &Token::Punct(Punct::Comma)
391 | &Token::Punct(Punct::Period) => (),
392 _ => return true,
393 },
394 &Token::Ident(_) => {
395 if let &Token::Punct(Punct::OpenBrace) = &token.token {
396 return true;
397 }
398 }
399 _ => (),
400 }
401 if is_identifier_like(token) && is_identifier_like(t) {
402 return true;
403 }
404 }
405
406 match &token.token {
407 &Token::Punct(Punct::AmpersandEqual)
408 | &Token::Punct(Punct::AsteriskEqual)
409 | &Token::Punct(Punct::CaretEqual)
410 | &Token::Punct(Punct::DashEqual)
411 | &Token::Punct(Punct::DoubleAsteriskEqual)
412 | &Token::Punct(Punct::Equal)
413 | &Token::Punct(Punct::ForwardSlashEqual)
414 | &Token::Punct(Punct::PercentEqual)
415 | &Token::Punct(Punct::PipeEqual)
416 | &Token::Punct(Punct::PlusEqual)
417 | &Token::Punct(Punct::QuestionMark) => true,
418 &Token::Punct(Punct::Ampersand)
419 | &Token::Punct(Punct::Asterisk)
420 | &Token::Punct(Punct::BangDoubleEqual)
421 | &Token::Punct(Punct::BangEqual)
422 | &Token::Punct(Punct::Caret)
423 | &Token::Punct(Punct::Dash)
424 | &Token::Punct(Punct::DoubleAmpersand)
425 | &Token::Punct(Punct::DoubleAsterisk)
426 | &Token::Punct(Punct::DoubleEqual)
427 | &Token::Punct(Punct::DoubleGreaterThan)
428 | &Token::Punct(Punct::DoubleGreaterThanEqual)
429 | &Token::Punct(Punct::DoubleLessThan)
430 | &Token::Punct(Punct::DoubleLessThanEqual)
431 | &Token::Punct(Punct::DoublePipe)
432 | &Token::Punct(Punct::GreaterThan)
433 | &Token::Punct(Punct::GreaterThanEqual)
434 | &Token::Punct(Punct::LessThan)
435 | &Token::Punct(Punct::LessThanEqual)
436 | &Token::Punct(Punct::Percent)
437 | &Token::Punct(Punct::Pipe)
438 | &Token::Punct(Punct::Plus) => last_token.is_some(),
439 _ => false,
440 }
441}
442
443fn increments_indent(token: &Tok) -> bool {
444 match &token.token {
445 &Token::Punct(Punct::OpenBrace) | &Token::Keyword(Keyword::Switch(_)) => true,
446 _ => token.starts_array_literal,
447 }
448}
449
450fn decrements_indent(token: &Tok, stack: &Stack) -> bool {
451 match &token.token {
452 &Token::Punct(Punct::CloseBrace) => true,
453 &Token::Punct(Punct::CloseBracket) => stack
454 .last()
455 .map(|v| v.starts_array_literal)
456 .unwrap_or(false),
457 _ => false,
458 }
459}
460
461fn prepend_white_space(
462 token: &Tok,
463 last_token: &Option<Tok>,
464 stack: &Stack,
465 mut added_newline: bool,
466 mut added_space: bool,
467 indent_level: u32,
468 out: &mut Writer,
469) {
470 if let Some(&Token::Punct(Punct::CloseBrace)) = last_token.as_ref().map(|v| &v.token) {
471 let start = last_token.as_ref().unwrap().start;
472 match &token.token {
473 &Token::Keyword(Keyword::While(_)) => {
474 if let Some(&Token::Keyword(Keyword::Do(_))) = top_token(stack) {
475 out.write(" ", start);
476 added_space = true;
477 } else {
478 out.write("\n", start);
479 added_newline = true;
480 }
481 }
482 &Token::Keyword(Keyword::Else(_))
483 | &Token::Keyword(Keyword::Catch(_))
484 | &Token::Keyword(Keyword::Finally(_)) => {
485 out.write(" ", start);
486 added_space = true;
487 }
488 &Token::Punct(Punct::OpenParen)
489 | &Token::Punct(Punct::SemiColon)
490 | &Token::Punct(Punct::Comma)
491 | &Token::Punct(Punct::CloseParen)
492 | &Token::Punct(Punct::Period)
493 | &Token::Template(_) => (),
494 _ => {
495 out.write("\n", start);
496 added_newline = true;
497 }
498 }
499 }
500
501 match &token.token {
502 &Token::Punct(Punct::Colon) => {
503 if let Some(&Token::Punct(Punct::QuestionMark)) = top_token(stack) {
504 out.write(" ", last_token.as_ref().unwrap().start);
505 added_space = true;
506 }
507 }
508 &Token::Keyword(Keyword::Else(_)) => match last_token.as_ref().map(|v| &v.token) {
509 Some(&Token::Punct(Punct::CloseBrace)) | Some(&Token::Punct(Punct::Period)) => (),
510 Some(_) => {
511 out.write(" ", last_token.as_ref().unwrap().start);
512 added_space = true;
513 }
514 None => (),
515 },
516 _ => (),
517 }
518
519 if is_asi(token, last_token) || decrements_indent(token, stack) {
520 if !added_newline {
521 out.write("\n", last_token.as_ref().unwrap().start);
522 added_newline = true;
523 }
524 }
525
526 if added_newline {
527 match &token.token {
528 &Token::Keyword(Keyword::Case(_)) | &Token::Keyword(Keyword::Default(_)) => {
529 out.write_indent(max(1, indent_level) - 1, token.start);
530 }
531 _ => {
532 out.write_indent(indent_level, token.start);
533 }
534 }
535 } else if !added_space && need_space_after(token, last_token) {
536 out.write(" ", last_token.as_ref().unwrap().start);
537 }
538}
539
540fn add_token(token: &Tok, out: &mut Writer) {
541 out.write(&token.token.to_string(), token.start);
542}
543
544fn belongs_on_stack(token: &Tok) -> bool {
545 match &token.token {
546 &Token::Keyword(Keyword::Case(_))
547 | &Token::Keyword(Keyword::Default(_))
548 | &Token::Keyword(Keyword::Do(_))
549 | &Token::Keyword(Keyword::Switch(_))
550 | &Token::Punct(Punct::OpenBrace)
551 | &Token::Punct(Punct::OpenParen)
552 | &Token::Punct(Punct::OpenBracket)
553 | &Token::Punct(Punct::QuestionMark) => true,
554 _ => false,
555 }
556}
557
558fn should_pop_stack(token: &Tok, stack: &Stack) -> bool {
559 match &token.token {
560 &Token::Keyword(Keyword::While(_)) => match top_token(stack) {
561 Some(&Token::Keyword(Keyword::Do(_))) => true,
562 _ => false,
563 },
564 &Token::Punct(Punct::CloseBracket)
565 | &Token::Punct(Punct::CloseParen)
566 | &Token::Punct(Punct::CloseBrace) => true,
567 &Token::Punct(Punct::Colon) => match top_token(stack) {
568 Some(&Token::Keyword(Keyword::Case(_)))
569 | Some(&Token::Keyword(Keyword::Default(_)))
570 | Some(&Token::Punct(Punct::QuestionMark)) => true,
571 _ => false,
572 },
573 _ => false,
574 }
575}
576
577fn add_comment(token: &Tok, next_token: Option<&Tok>, indent_level: u32, out: &mut Writer) -> bool {
578 out.write_indent(indent_level, token.start);
579 let comment = if let &Token::Comment(ref c) = &token.token {
580 c
581 } else {
582 panic!("Must be a comment");
583 };
584 let need_new_line = match comment.kind {
585 CommentKind::Multi | CommentKind::Html => {
586 out.write_new("/*");
587 let mut first = true;
588 for line in comment.content.lines() {
589 if first {
590 first = false;
591 } else {
592 out.write_indent(indent_level, token.start);
593 out.write_new(" ");
594 }
595 out.write_new(line);
596 }
597 out.write_indent(indent_level, token.start);
598 out.write_new("*/");
599 next_token
600 .map(|t| t.start.line != token.start.line)
601 .unwrap_or(false)
602 }
603 CommentKind::Hashbang | CommentKind::Single => {
604 out.write_new(&comment.to_string());
605 true
606 }
607 };
608 if need_new_line {
609 out.write_new("\n");
610 } else {
611 out.write_new(" ");
612 }
613 need_new_line
614}
615
616fn convert_position(pos: Position) -> SourceCoord {
617 SourceCoord {
618 line: SourceMapLine(pos.line as u32 - 1),
619 column: SourceMapColumn(pos.column as u32 - 1),
620 }
621}
622
623fn convert_token<'a>(item: Item<Token<&'a str>>) -> Tok<'a> {
624 debug!("token: {:?} -> {:?}", item.location.start, item.token);
625 Tok {
626 token: item.token,
627 start: convert_position(item.location.start),
628 end: convert_position(item.location.end),
629 starts_array_literal: false,
630 }
631}
632
633pub fn prettyprint(source: &str) -> (String, Vec<SourceMapping>) {
647 let mut indent_level = 0;
648 let mut out = Writer::new(2);
649 let mut added_newline = false;
650 let mut added_space = false;
651 let mut stack: Stack = Vec::new();
652 let mut scanner = Scanner::new(source)
653 .filter_map(|v| match v {
654 Ok(v) => Some(convert_token(v)),
655 Err(_) => None,
656 })
657 .peekable();
658 let mut last_token: Option<Tok> = None;
659
660 while let Some(mut token) = scanner.next() {
661 let next_token = scanner.peek();
662 match &token.token {
663 &Token::Comment(_) => {
664 let comment_indent_level = if last_token
665 .as_ref()
666 .map(|v| v.end.line == token.start.line)
667 .unwrap_or(false)
668 {
669 out.write_new(" ");
670 0
671 } else {
672 indent_level
673 };
674 added_newline = add_comment(&token, next_token, comment_indent_level, &mut out);
675 added_space = !added_newline;
676 continue;
677 }
678 &Token::EoF => break,
679 _ => (),
680 }
681
682 token.starts_array_literal = starts_array_literal(&token, &last_token);
683
684 if belongs_on_stack(&token) {
685 stack.push(token.clone());
686 }
687
688 if decrements_indent(&token, &stack) {
689 indent_level = max(1, indent_level) - 1;
690 if let &Token::Punct(Punct::CloseBrace) = &token.token {
691 if stack.len() >= 2 {
692 if let &Token::Keyword(Keyword::Switch(_)) = &stack[stack.len() - 2].token {
693 indent_level = max(1, indent_level) - 1;
694 }
695 }
696 }
697 }
698
699 prepend_white_space(
700 &token,
701 &last_token,
702 &stack,
703 added_newline,
704 added_space,
705 indent_level,
706 &mut out,
707 );
708 add_token(&token, &mut out);
709 added_space = false;
710 let mut same_line_comment = false;
711 if let Some(&Token::Comment(_)) = next_token.as_ref().map(|v| &v.token) {
712 if next_token.unwrap().start.line == token.end.line {
713 same_line_comment = true;
714 }
715 }
716 if !same_line_comment {
717 added_newline = append_newline(&token, &stack, &mut out);
718 }
719
720 if should_pop_stack(&token, &stack) {
721 stack.pop();
722 if let &Token::Punct(Punct::CloseBrace) = &token.token {
723 if let Some(&Token::Keyword(Keyword::Switch(_))) = stack.last().map(|v| &v.token) {
724 stack.pop();
725 }
726 }
727 }
728
729 if increments_indent(&token) {
730 indent_level += 1;
731 }
732
733 last_token = Some(token);
734 }
735
736 if !added_newline {
737 out.write_new("\n");
738 }
739
740 (out.buffer, out.mappings)
741}