1# The MIT License (MIT)
2#
3# Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
4#
5# Permission is hereby granted, free of charge, to any person
6# obtaining a copy of this software and associated documentation files
7# (the "Software"), to deal in the Software without restriction,
8# including without limitation the rights to use, copy, modify, merge,
9# publish, distribute, sublicense, and/or sell copies of the Software,
10# and to permit persons to whom the Software is furnished to do so,
11# subject to the following conditions:
12#
13# The above copyright notice and this permission notice shall be
14# included in all copies or substantial portions of the Software.
15#
16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23# SOFTWARE.
24
25import re
26import string
27import copy
28from ..core.token import Token
29from .tokenizer import Tokenizer
30from .tokenizer import TOKEN
31from .options import BeautifierOptions
32from ..core.output import Output
33
34
35def default_options():
36    return BeautifierOptions()
37
38
39class BeautifierFlags:
40    def __init__(self, mode):
41        self.mode = mode
42        self.parent = None
43        self.last_token = Token(TOKEN.START_BLOCK, '')
44        self.last_word = ''
45        self.declaration_statement = False
46        self.declaration_assignment = False
47        self.multiline_frame = False
48        self.inline_frame = False
49        self.if_block = False
50        self.else_block = False
51        self.do_block = False
52        self.do_while = False
53        self.import_block = False
54        self.in_case = False
55        self.in_case_statement = False
56        self.case_body = False
57        self.indentation_level = 0
58        self.alignment = 0
59        self.line_indent_level = 0
60        self.start_line_index = 0
61        self.ternary_depth = 0
62
63    def apply_base(self, flags_base, added_newline):
64        next_indent_level = flags_base.indentation_level
65        if not added_newline and \
66                flags_base.line_indent_level > next_indent_level:
67            next_indent_level = flags_base.line_indent_level
68
69        self.parent = flags_base
70        self.last_token = flags_base.last_token
71        self.last_word = flags_base.last_word
72        self.indentation_level = next_indent_level
73
74
75OPERATOR_POSITION = {
76    'before_newline': 'before-newline',
77    'after_newline': 'after-newline',
78    'preserve_newline': 'preserve-newline'
79}
80OPERATOR_POSITION_BEFORE_OR_PRESERVE = [
81    OPERATOR_POSITION['before_newline'],
82    OPERATOR_POSITION['preserve_newline']]
83
84
85class MODE:
86    BlockStatement, Statement, ObjectLiteral, ArrayLiteral, \
87        ForInitializer, Conditional, Expression = range(7)
88
89
90def remove_redundant_indentation(output, frame):
91    # This implementation is effective but has some issues:
92    #     - can cause line wrap to happen too soon due to indent removal
93    #           after wrap points are calculated
94    # These issues are minor compared to ugly indentation.
95
96    if frame.multiline_frame or \
97            frame.mode == MODE.ForInitializer or \
98            frame.mode == MODE.Conditional:
99        return
100
101    # remove one indent from each line inside this section
102    output.remove_indent(frame.start_line_index)
103
104
105def reserved_word(token, word):
106    return token and token.type == TOKEN.RESERVED and token.text == word
107
108
109def reserved_array(token, words):
110    return token and token.type == TOKEN.RESERVED and token.text in words
111
112
113_special_word_set = frozenset([
114    'case',
115    'return',
116    'do',
117    'if',
118    'throw',
119    'else',
120    'await',
121    'break',
122    'continue',
123    'async'])
124
125
126class Beautifier:
127
128    def __init__(self, opts=None):
129        import jsbeautifier.javascript.acorn as acorn
130        self.acorn = acorn
131        self._options = BeautifierOptions(opts)
132
133        self._blank_state()
134
135    def _blank_state(self, js_source_text=None):
136        if js_source_text is None:
137            js_source_text = ''
138
139        # internal flags
140        self._flags = None
141        self._previous_flags = None
142        self._flag_store = []
143        self._tokens = None
144
145        if self._options.eol == 'auto':
146            self._options.eol = '\n'
147            if self.acorn.lineBreak.search(js_source_text or ''):
148                self._options.eol = self.acorn.lineBreak.search(
149                    js_source_text).group()
150
151
152        baseIndentString = re.search("^[\t ]*", js_source_text).group(0)
153        self._last_last_text = ''         # pre-last token text
154
155
156        self._output = Output(self._options, baseIndentString)
157        # If testing the ignore directive, start with output disable set to
158        # true
159        self._output.raw = self._options.test_output_raw
160
161        self.set_mode(MODE.BlockStatement)
162        return js_source_text
163
164    def beautify(self, source_text='', opts=None):
165        if opts is not None:
166            self._options = BeautifierOptions(opts)
167
168
169        source_text = source_text or ''
170        if self._options.disabled:
171            return source_text
172
173        source_text = self._blank_state(source_text)
174
175        source_text = self.unpack(source_text, self._options.eval_code)
176
177        self._tokens = Tokenizer(source_text, self._options).tokenize()
178
179        for current_token in self._tokens:
180            self.handle_token(current_token)
181
182            self._last_last_text = self._flags.last_token.text
183            self._flags.last_token = current_token
184
185        sweet_code = self._output.get_code(self._options.eol)
186
187        return sweet_code
188
189    def handle_token(self, current_token, preserve_statement_flags=False):
190        if current_token.type == TOKEN.START_EXPR:
191            self.handle_start_expr(current_token)
192        elif current_token.type == TOKEN.END_EXPR:
193            self.handle_end_expr(current_token)
194        elif current_token.type == TOKEN.START_BLOCK:
195            self.handle_start_block(current_token)
196        elif current_token.type == TOKEN.END_BLOCK:
197            self.handle_end_block(current_token)
198        elif current_token.type == TOKEN.WORD:
199            self.handle_word(current_token)
200        elif current_token.type == TOKEN.RESERVED:
201            self.handle_word(current_token)
202        elif current_token.type == TOKEN.SEMICOLON:
203            self.handle_semicolon(current_token)
204        elif current_token.type == TOKEN.STRING:
205            self.handle_string(current_token)
206        elif current_token.type == TOKEN.EQUALS:
207            self.handle_equals(current_token)
208        elif current_token.type == TOKEN.OPERATOR:
209            self.handle_operator(current_token)
210        elif current_token.type == TOKEN.COMMA:
211            self.handle_comma(current_token)
212        elif current_token.type == TOKEN.BLOCK_COMMENT:
213            self.handle_block_comment(current_token, preserve_statement_flags)
214        elif current_token.type == TOKEN.COMMENT:
215            self.handle_comment(current_token, preserve_statement_flags)
216        elif current_token.type == TOKEN.DOT:
217            self.handle_dot(current_token)
218        elif current_token.type == TOKEN.EOF:
219            self.handle_eof(current_token)
220        elif current_token.type == TOKEN.UNKNOWN:
221            self.handle_unknown(current_token, preserve_statement_flags)
222        else:
223            self.handle_unknown(current_token, preserve_statement_flags)
224
225    def handle_whitespace_and_comments(
226            self, current_token, preserve_statement_flags=False):
227        newlines = current_token.newlines
228        keep_whitespace = self._options.keep_array_indentation and self.is_array(
229            self._flags.mode)
230
231        if current_token.comments_before is not None:
232            for comment_token in current_token.comments_before:
233                    # The cleanest handling of inline comments is to treat them
234                    # as though they aren't there.
235                # Just continue formatting and the behavior should be logical.
236                # Also ignore unknown tokens.  Again, this should result in better
237                # behavior.
238                self.handle_whitespace_and_comments(
239                    comment_token, preserve_statement_flags)
240                self.handle_token(comment_token, preserve_statement_flags)
241
242        if keep_whitespace:
243            for i in range(newlines):
244                self.print_newline(i > 0, preserve_statement_flags)
245        else:  # not keep_whitespace
246            if self._options.max_preserve_newlines != 0 and newlines > self._options.max_preserve_newlines:
247                newlines = self._options.max_preserve_newlines
248
249            if self._options.preserve_newlines and newlines > 1:
250                self.print_newline(False, preserve_statement_flags)
251                for i in range(1, newlines):
252                    self.print_newline(True, preserve_statement_flags)
253
254    def unpack(self, source, evalcode=False):
255        import jsbeautifier.unpackers as unpackers
256        try:
257            return unpackers.run(source, evalcode)
258        except unpackers.UnpackingError:
259            return source
260
261    def is_array(self, mode):
262        return mode == MODE.ArrayLiteral
263
264    def is_expression(self, mode):
265        return mode == MODE.Expression or mode == MODE.ForInitializer or mode == MODE.Conditional
266
267    _newline_restricted_tokens = frozenset([
268        'async',
269        'break',
270        'continue',
271        'return',
272        'throw',
273        'yield'])
274
275    def allow_wrap_or_preserved_newline(
276            self, current_token, force_linewrap=False):
277        # never wrap the first token of a line.
278        if self._output.just_added_newline():
279            return
280
281        shouldPreserveOrForce = (
282            self._options.preserve_newlines and current_token.newlines) or force_linewrap
283        operatorLogicApplies = self._flags.last_token.text in Tokenizer.positionable_operators or current_token.text in Tokenizer.positionable_operators
284
285        if operatorLogicApplies:
286            shouldPrintOperatorNewline = (self._flags.last_token.text in Tokenizer.positionable_operators and self._options.operator_position in OPERATOR_POSITION_BEFORE_OR_PRESERVE) \
287                or current_token.text in Tokenizer.positionable_operators
288            shouldPreserveOrForce = shouldPreserveOrForce and shouldPrintOperatorNewline
289
290        if shouldPreserveOrForce:
291            self.print_newline(preserve_statement_flags=True)
292        elif self._options.wrap_line_length > 0:
293            if reserved_array(self._flags.last_token, self._newline_restricted_tokens):
294                # These tokens should never have a newline inserted between
295                # them and the following expression.
296                return
297            self._output.set_wrap_point()
298
299    def print_newline(
300            self,
301            force_newline=False,
302            preserve_statement_flags=False):
303        if not preserve_statement_flags:
304            if self._flags.last_token.text != ';' and self._flags.last_token.text != ',' and self._flags.last_token.text != '=' and (
305                    self._flags.last_token.type != TOKEN.OPERATOR or self._flags.last_token.text == '--' or self._flags.last_token.text == '++'):
306                next_token = self._tokens.peek()
307                while (self._flags.mode == MODE.Statement and \
308                        not (self._flags.if_block and reserved_word(next_token, 'else')) and \
309                        not self._flags.do_block):
310                    self.restore_mode()
311
312        if self._output.add_new_line(force_newline):
313            self._flags.multiline_frame = True
314
315    def print_token_line_indentation(self, current_token):
316        if self._output.just_added_newline():
317            line = self._output.current_line
318            if self._options.keep_array_indentation and \
319                current_token.newlines and \
320                (self.is_array(self._flags.mode) or
321                        current_token.text == '['):
322                line.set_indent(-1)
323                line.push(current_token.whitespace_before)
324                self._output.space_before_token = False
325            elif self._output.set_indent(self._flags.indentation_level,
326                            self._flags.alignment):
327                self._flags.line_indent_level = self._flags.indentation_level
328
329    def print_token(self, current_token, s=None):
330        if self._output.raw:
331            self._output.add_raw_token(current_token)
332            return
333
334        if self._options.comma_first and current_token.previous and \
335            current_token.previous.type == TOKEN.COMMA and \
336                self._output.just_added_newline():
337            if self._output.previous_line.last() == ',':
338                # if the comma was already at the start of the line,
339                # pull back onto that line and reprint the indentation
340                popped = self._output.previous_line.pop()
341                if self._output.previous_line.is_empty():
342                    self._output.previous_line.push(popped)
343                    self._output.trim(True)
344                    self._output.current_line.pop()
345                    self._output.trim()
346
347                # add the comma in front of the next token
348                self.print_token_line_indentation(current_token)
349                self._output.add_token(',')
350                self._output.space_before_token = True
351
352        if s is None:
353            s = current_token.text
354
355        self.print_token_line_indentation(current_token)
356        self._output.non_breaking_space = True
357        self._output.add_token(s)
358        if self._output.previous_token_wrapped:
359            self._flags.multiline_frame = True
360
361    def indent(self):
362        self._flags.indentation_level += 1
363        self._output.set_indent(self._flags.indentation_level,
364            self._flags.alignment)
365
366    def deindent(self):
367        allow_deindent = self._flags.indentation_level > 0 and (
368            (self._flags.parent is None) or self._flags.indentation_level > self._flags.parent.indentation_level)
369
370        if allow_deindent:
371            self._flags.indentation_level -= 1
372
373        self._output.set_indent(self._flags.indentation_level,
374            self._flags.alignment)
375
376
377    def set_mode(self, mode):
378        if self._flags:
379            self._flag_store.append(self._flags)
380            self._previous_flags = self._flags
381        else:
382            self._previous_flags = BeautifierFlags(mode)
383
384        self._flags = BeautifierFlags(mode)
385        self._flags.apply_base(
386            self._previous_flags,
387            self._output.just_added_newline())
388        self._flags.start_line_index = self._output.get_line_number()
389
390        self._output.set_indent(self._flags.indentation_level,
391            self._flags.alignment)
392
393    def restore_mode(self):
394        if len(self._flag_store) > 0:
395            self._previous_flags = self._flags
396            self._flags = self._flag_store.pop()
397            if self._previous_flags.mode == MODE.Statement:
398                remove_redundant_indentation(self._output, self._previous_flags)
399
400        self._output.set_indent(self._flags.indentation_level,
401            self._flags.alignment)
402
403    def start_of_object_property(self):
404        return self._flags.parent.mode == MODE.ObjectLiteral and self._flags.mode == MODE.Statement and (
405            (self._flags.last_token.text == ':' and self._flags.ternary_depth == 0) or (
406                reserved_array(self._flags.last_token, ['get', 'set'])))
407
408    def start_of_statement(self, current_token):
409        start = False
410        start = start or (
411            reserved_array(self._flags.last_token, ['var', 'let', 'const']) and
412            current_token.type == TOKEN.WORD)
413        start = start or reserved_word(self._flags.last_token, 'do')
414        start = start or (
415            not (self._flags.parent.mode == MODE.ObjectLiteral and self._flags.mode == MODE.Statement) and
416            reserved_array(self._flags.last_token, self._newline_restricted_tokens) and
417            not current_token.newlines)
418        start = start or (
419            reserved_word(self._flags.last_token, 'else') and not (
420                reserved_word(current_token, 'if') and \
421                    current_token.comments_before is None))
422        start = start or (self._flags.last_token.type == TOKEN.END_EXPR and (
423            self._previous_flags.mode == MODE.ForInitializer or self._previous_flags.mode == MODE.Conditional))
424        start = start or (self._flags.last_token.type == TOKEN.WORD and self._flags.mode == MODE.BlockStatement
425                          and not self._flags.in_case
426                          and not (current_token.text == '--' or current_token.text == '++')
427                          and self._last_last_text != 'function'
428                          and current_token.type != TOKEN.WORD and current_token.type != TOKEN.RESERVED)
429        start = start or (
430            self._flags.mode == MODE.ObjectLiteral and (
431                (self._flags.last_token.text == ':' and self._flags.ternary_depth == 0) or (
432                    reserved_array(self._flags.last_token, ['get', 'set']))))
433
434        if (start):
435            self.set_mode(MODE.Statement)
436            self.indent()
437
438            self.handle_whitespace_and_comments(current_token, True)
439
440            # Issue #276:
441            # If starting a new statement with [if, for, while, do], push to a new line.
442            # if (a) if (b) if(c) d(); else e(); else f();
443            if not self.start_of_object_property():
444                self.allow_wrap_or_preserved_newline(
445                    current_token, reserved_array(current_token, ['do', 'for', 'if', 'while']))
446            return True
447        else:
448            return False
449
450    def handle_start_expr(self, current_token):
451        if self.start_of_statement(current_token):
452            # The conditional starts the statement if appropriate.
453            pass
454        else:
455            self.handle_whitespace_and_comments(current_token)
456
457        next_mode = MODE.Expression
458
459        if current_token.text == '[':
460            if self._flags.last_token.type == TOKEN.WORD or self._flags.last_token.text == ')':
461                if reserved_array(self._flags.last_token, Tokenizer.line_starters):
462                    self._output.space_before_token = True
463                self.print_token(current_token)
464                self.set_mode(next_mode)
465                self.indent()
466                if self._options.space_in_paren:
467                    self._output.space_before_token = True
468                return
469
470            next_mode = MODE.ArrayLiteral
471
472            if self.is_array(self._flags.mode):
473                if self._flags.last_token.text == '[' or (
474                    self._flags.last_token.text == ',' and (
475                        self._last_last_text == ']' or self._last_last_text == '}')):
476                    # ], [ goes to a new line
477                    # }, [ goes to a new line
478                    if not self._options.keep_array_indentation:
479                        self.print_newline()
480
481            if self._flags.last_token.type not in [
482                    TOKEN.START_EXPR,
483                    TOKEN.END_EXPR,
484                    TOKEN.WORD,
485                    TOKEN.OPERATOR]:
486                self._output.space_before_token = True
487
488        else:
489            if self._flags.last_token.type == TOKEN.RESERVED:
490                if self._flags.last_token.text == 'for':
491                    self._output.space_before_token = self._options.space_before_conditional
492                    next_mode = MODE.ForInitializer
493                elif self._flags.last_token.text in ['if', 'while']:
494                    self._output.space_before_token = self._options.space_before_conditional
495                    next_mode = MODE.Conditional
496                elif self._flags.last_word in ['await', 'async']:
497                    # Should be a space between await and an IIFE, or async and
498                    # an arrow function
499                    self._output.space_before_token = True
500                elif self._flags.last_token.text == 'import' and current_token.whitespace_before == '':
501                    self._output.space_before_token = False
502                elif self._flags.last_token.text in Tokenizer.line_starters or self._flags.last_token.text == 'catch':
503                    self._output.space_before_token = True
504
505            elif self._flags.last_token.type in [TOKEN.EQUALS, TOKEN.OPERATOR]:
506                # Support of this kind of newline preservation:
507                # a = (b &&
508                #     (c || d));
509                if not self.start_of_object_property():
510                    self.allow_wrap_or_preserved_newline(current_token)
511            elif self._flags.last_token.type == TOKEN.WORD:
512                self._output.space_before_token = False
513                # function name() vs function name ()
514                # function* name() vs function* name ()
515                # async name() vs async name ()
516                # In ES6, you can also define the method properties of an object
517                # var obj = {a: function() {}}
518                # It can be abbreviated
519                # var obj = {a() {}}
520                # var obj = { a() {}} vs var obj = { a () {}}
521                # var obj = { * a() {}} vs var obj = { * a () {}}
522                peek_back_two = self._tokens.peek(-3)
523                if self._options.space_after_named_function and peek_back_two:
524                    # peek starts at next character so -1 is current token
525                    peek_back_three = self._tokens.peek(-4)
526                    if reserved_array(peek_back_two, ['async', 'function']) or (
527                        peek_back_two.text == '*' and
528                            reserved_array(peek_back_three, ['async', 'function'])):
529                        self._output.space_before_token = True
530                    elif self._flags.mode == MODE.ObjectLiteral:
531                        if (peek_back_two.text == '{' or peek_back_two.text == ',') or (
532                            peek_back_two.text == '*' and (
533                                peek_back_three.text == '{' or peek_back_three.text == ',')):
534                            self._output.space_before_token = True
535            else:
536                # Support preserving wrapped arrow function expressions
537                # a.b('c',
538                #     () => d.e
539                # )
540                self.allow_wrap_or_preserved_newline(current_token)
541
542            # function() vs function (), typeof() vs typeof ()
543            # function*() vs function* (), yield*() vs yield* ()
544            if (
545                self._flags.last_token.type == TOKEN.RESERVED and (
546                    self._flags.last_word == 'function' or self._flags.last_word == 'typeof')) or (
547                self._flags.last_token.text == '*' and (
548                    self._last_last_text in [
549                    'function', 'yield'] or (
550                        self._flags.mode == MODE.ObjectLiteral and self._last_last_text in [
551                            '{', ',']))):
552                self._output.space_before_token = self._options.space_after_anon_function
553
554        if self._flags.last_token.text == ';' or self._flags.last_token.type == TOKEN.START_BLOCK:
555            self.print_newline()
556        elif self._flags.last_token.type in [TOKEN.END_EXPR, TOKEN.START_EXPR, TOKEN.END_BLOCK, TOKEN.COMMA] or self._flags.last_token.text == '.':
557            # do nothing on (( and )( and ][ and ]( and .(
558            # TODO: Consider whether forcing this is required.  Review failing
559            # tests when removed.
560            self.allow_wrap_or_preserved_newline(
561                current_token, current_token.newlines)
562
563        self.print_token(current_token)
564        self.set_mode(next_mode)
565
566        if self._options.space_in_paren:
567            self._output.space_before_token = True
568
569        # In all cases, if we newline while inside an expression it should be
570        # indented.
571        self.indent()
572
573    def handle_end_expr(self, current_token):
574        # statements inside expressions are not valid syntax, but...
575        # statements must all be closed when their container closes
576        while self._flags.mode == MODE.Statement:
577            self.restore_mode()
578
579        self.handle_whitespace_and_comments(current_token)
580
581        if self._flags.multiline_frame:
582            self.allow_wrap_or_preserved_newline(
583                current_token, current_token.text == ']' and self.is_array(
584                    self._flags.mode) and not self._options.keep_array_indentation)
585
586        if self._options.space_in_paren:
587            if self._flags.last_token.type == TOKEN.START_EXPR and not self._options.space_in_empty_paren:
588                # empty parens are always "()" and "[]", not "( )" or "[ ]"
589                self._output.space_before_token = False
590                self._output.trim()
591            else:
592                self._output.space_before_token = True
593
594        self.deindent()
595        self.print_token(current_token)
596        self.restore_mode()
597
598        remove_redundant_indentation(self._output, self._previous_flags)
599
600        # do {} while () // no statement required after
601        if self._flags.do_while and self._previous_flags.mode == MODE.Conditional:
602            self._previous_flags.mode = MODE.Expression
603            self._flags.do_block = False
604            self._flags.do_while = False
605
606    def handle_start_block(self, current_token):
607        self.handle_whitespace_and_comments(current_token)
608
609        # Check if this is a BlockStatement that should be treated as a
610        # ObjectLiteral
611        next_token = self._tokens.peek()
612        second_token = self._tokens.peek(1)
613        if self._flags.last_word == 'switch' and \
614                self._flags.last_token.type == TOKEN.END_EXPR:
615            self.set_mode(MODE.BlockStatement)
616            self._flags.in_case_statement = True
617        elif self._flags.case_body:
618            self.set_mode(MODE.BlockStatement)
619        elif second_token is not None and (
620            (second_token.text in [
621                ':',
622                ','] and next_token.type in [
623                TOKEN.STRING,
624                TOKEN.WORD,
625                TOKEN.RESERVED]) or (
626                next_token.text in [
627                    'get',
628                    'set',
629                    '...'] and second_token.type in [
630                        TOKEN.WORD,
631                    TOKEN.RESERVED])):
632            # We don't support TypeScript,but we didn't break it for a very long time.
633            # We'll try to keep not breaking it.
634            if self._last_last_text not in ['class', 'interface']:
635                self.set_mode(MODE.ObjectLiteral)
636            else:
637                self.set_mode(MODE.BlockStatement)
638        elif self._flags.last_token.type == TOKEN.OPERATOR and self._flags.last_token.text == '=>':
639            # arrow function: (param1, paramN) => { statements }
640            self.set_mode(MODE.BlockStatement)
641        elif self._flags.last_token.type in [TOKEN.EQUALS, TOKEN.START_EXPR, TOKEN.COMMA, TOKEN.OPERATOR] or \
642                reserved_array(self._flags.last_token, ['return', 'throw', 'import', 'default']):
643            # Detecting shorthand function syntax is difficult by scanning forward,
644            #     so check the surrounding context.
645            # If the block is being returned, imported, export default, passed as arg,
646            # assigned with = or assigned in a nested object, treat as an
647            # ObjectLiteral.
648            self.set_mode(MODE.ObjectLiteral)
649        else:
650            self.set_mode(MODE.BlockStatement)
651
652        empty_braces = (next_token is not None) and \
653            next_token.comments_before is None and next_token.text == '}'
654        empty_anonymous_function = empty_braces and self._flags.last_word == 'function' and \
655            self._flags.last_token.type == TOKEN.END_EXPR
656
657        if self._options.brace_preserve_inline:  # check for inline, set inline_frame if so
658            # search forward for newline wanted inside this block
659            index = 0
660            check_token = None
661            self._flags.inline_frame = True
662            do_loop = True
663            while (do_loop):
664                index += 1
665                check_token = self._tokens.peek(index - 1)
666                if check_token.newlines:
667                    self._flags.inline_frame = False
668
669                do_loop = (
670    check_token.type != TOKEN.EOF and not (
671         check_token.type == TOKEN.END_BLOCK and check_token.opened == current_token))
672
673        if (self._options.brace_style == 'expand' or (self._options.brace_style ==
674                                                  'none' and current_token.newlines)) and not self._flags.inline_frame:
675            if self._flags.last_token.type != TOKEN.OPERATOR and (
676                empty_anonymous_function or self._flags.last_token.type == TOKEN.EQUALS or (
677                    reserved_array(self._flags.last_token, _special_word_set) and self._flags.last_token.text != 'else')):
678                self._output.space_before_token = True
679            else:
680                self.print_newline(preserve_statement_flags=True)
681        else:  # collapse || inline_frame
682            if self.is_array(
683    self._previous_flags.mode) and (
684         self._flags.last_token.type == TOKEN.START_EXPR or self._flags.last_token.type == TOKEN.COMMA):
685                # if we're preserving inline,
686                # allow newline between comma and next brace.
687                if self._flags.inline_frame:
688                    self.allow_wrap_or_preserved_newline(current_token)
689                    self._flags.inline_frame = True
690                    self._previous_flags.multiline_frame = self._previous_flags.multiline_frame or self._flags.multiline_frame
691                    self._flags.multiline_frame = False
692                elif self._flags.last_token.type == TOKEN.COMMA:
693                    self._output.space_before_token = True
694
695            elif self._flags.last_token.type not in [TOKEN.OPERATOR, TOKEN.START_EXPR]:
696                if self._flags.last_token.type == TOKEN.START_BLOCK and not self._flags.inline_frame:
697                    self.print_newline()
698                else:
699                    self._output.space_before_token = True
700
701        self.print_token(current_token)
702        self.indent()
703
704        # Except for specific cases, open braces are followed by a new line.
705        if not empty_braces and not (
706                self._options.brace_preserve_inline and
707                    self._flags.inline_frame):
708            self.print_newline()
709
710
711    def handle_end_block(self, current_token):
712        # statements must all be closed when their container closes
713        self.handle_whitespace_and_comments(current_token)
714
715        while self._flags.mode == MODE.Statement:
716            self.restore_mode()
717
718        empty_braces = self._flags.last_token.type == TOKEN.START_BLOCK
719
720        # try inline_frame (only set if opt.braces-preserve-inline) first
721        if self._flags.inline_frame and not empty_braces:
722            self._output.space_before_token = True
723        elif self._options.brace_style == 'expand':
724            if not empty_braces:
725                self.print_newline()
726        else:
727            # skip {}
728            if not empty_braces:
729                if self.is_array(
730                        self._flags.mode) and self._options.keep_array_indentation:
731                    self._options.keep_array_indentation = False
732                    self.print_newline()
733                    self._options.keep_array_indentation = True
734                else:
735                    self.print_newline()
736
737        self.restore_mode()
738        self.print_token(current_token)
739
740    def handle_word(self, current_token):
741        if current_token.type == TOKEN.RESERVED:
742            if current_token.text in [
743                    'set', 'get'] and self._flags.mode != MODE.ObjectLiteral:
744                current_token.type = TOKEN.WORD
745            elif current_token.text == 'import' and self._tokens.peek().text == '(':
746                current_token.type = TOKEN.WORD
747            elif current_token.text in ['as', 'from'] and not self._flags.import_block:
748                current_token.type = TOKEN.WORD
749            elif self._flags.mode == MODE.ObjectLiteral:
750                next_token = self._tokens.peek()
751                if next_token.text == ':':
752                    current_token.type = TOKEN.WORD
753
754        if self.start_of_statement(current_token):
755            # The conditional starts the statement if appropriate.
756            if reserved_array(self._flags.last_token, ['var', 'let', 'const']) and \
757                    current_token.type == TOKEN.WORD:
758                self._flags.declaration_statement = True
759
760        elif current_token.newlines and \
761                not self.is_expression(self._flags.mode) and \
762                (self._flags.last_token.type != TOKEN.OPERATOR or (self._flags.last_token.text == '--' or self._flags.last_token.text == '++')) and \
763                self._flags.last_token.type != TOKEN.EQUALS and \
764                (self._options.preserve_newlines or not reserved_array(self._flags.last_token, ['var', 'let', 'const', 'set', 'get'])):
765            self.handle_whitespace_and_comments(current_token)
766            self.print_newline()
767        else:
768            self.handle_whitespace_and_comments(current_token)
769
770        if self._flags.do_block and not self._flags.do_while:
771            if reserved_word(current_token, 'while'):
772                # do {} ## while ()
773                self._output.space_before_token = True
774                self.print_token(current_token)
775                self._output.space_before_token = True
776                self._flags.do_while = True
777                return
778            else:
779                # do {} should always have while as the next word.
780                # if we don't see the expected while, recover
781                self.print_newline()
782                self._flags.do_block = False
783
784        # if may be followed by else, or not
785        # Bare/inline ifs are tricky
786        # Need to unwind the modes correctly: if (a) if (b) c(); else d(); else
787        # e();
788        if self._flags.if_block:
789            if (not self._flags.else_block) and reserved_word(current_token, 'else'):
790                self._flags.else_block = True
791            else:
792                while self._flags.mode == MODE.Statement:
793                    self.restore_mode()
794
795                self._flags.if_block = False
796
797        if self._flags.in_case_statement and reserved_array(current_token, ['case', 'default']):
798            self.print_newline()
799            if self._flags.last_token.type != TOKEN.END_BLOCK and (
800                    self._flags.case_body or self._options.jslint_happy):
801                self.deindent()
802            self._flags.case_body = False
803            self.print_token(current_token)
804            self._flags.in_case = True
805            return
806
807        if self._flags.last_token.type in [
808                TOKEN.COMMA,
809                TOKEN.START_EXPR,
810                TOKEN.EQUALS,
811                TOKEN.OPERATOR]:
812            if not self.start_of_object_property():
813                self.allow_wrap_or_preserved_newline(current_token)
814
815        if reserved_word(current_token, 'function'):
816            if (self._flags.last_token.text in ['}', ';'] or (self._output.just_added_newline() and not (
817                    self._flags.last_token.text in ['(', '[', '{', ':', '=', ','] or self._flags.last_token.type == TOKEN.OPERATOR))):
818                # make sure there is a nice clean space of at least one blank line
819                # before a new function definition, except in arrays
820                if not self._output.just_added_blankline() and \
821                        current_token.comments_before is None:
822                    self.print_newline()
823                    self.print_newline(True)
824
825            if self._flags.last_token.type == TOKEN.RESERVED or self._flags.last_token.type == TOKEN.WORD:
826                if reserved_array(self._flags.last_token, ['get', 'set', 'new', 'export']) or \
827                        reserved_array(self._flags.last_token, self._newline_restricted_tokens):
828                    self._output.space_before_token = True
829                elif reserved_word(self._flags.last_token, 'default') and self._last_last_text == 'export':
830                    self._output.space_before_token = True
831                elif self._flags.last_token.text == 'declare':
832                    # accomodates Typescript declare function formatting
833                    self._output.space_before_token = True
834                else:
835                    self.print_newline()
836            elif self._flags.last_token.type == TOKEN.OPERATOR or self._flags.last_token.text == '=':
837                # foo = function
838                self._output.space_before_token = True
839            elif not self._flags.multiline_frame and (self.is_expression(self._flags.mode) or self.is_array(self._flags.mode)):
840                # (function
841                pass
842            else:
843                self.print_newline()
844
845            self.print_token(current_token)
846            self._flags.last_word = current_token.text
847            return
848
849        prefix = 'NONE'
850
851        if self._flags.last_token.type == TOKEN.END_BLOCK:
852            if self._previous_flags.inline_frame:
853                prefix = 'SPACE'
854            elif not reserved_array(current_token, ['else', 'catch', 'finally', 'from']):
855                prefix = 'NEWLINE'
856            else:
857                if self._options.brace_style in ['expand', 'end-expand'] or (
858                        self._options.brace_style == 'none' and current_token.newlines):
859                    prefix = 'NEWLINE'
860                else:
861                    prefix = 'SPACE'
862                    self._output.space_before_token = True
863        elif self._flags.last_token.type == TOKEN.SEMICOLON and self._flags.mode == MODE.BlockStatement:
864            # TODO: Should this be for STATEMENT as well?
865            prefix = 'NEWLINE'
866        elif self._flags.last_token.type == TOKEN.SEMICOLON and self.is_expression(self._flags.mode):
867            prefix = 'SPACE'
868        elif self._flags.last_token.type == TOKEN.STRING:
869            prefix = 'NEWLINE'
870        elif self._flags.last_token.type == TOKEN.RESERVED or self._flags.last_token.type == TOKEN.WORD or \
871            (self._flags.last_token.text == '*' and (
872                self._last_last_text in ['function', 'yield'] or
873                (self._flags.mode == MODE.ObjectLiteral and self._last_last_text in ['{', ',']))):
874            prefix = 'SPACE'
875        elif self._flags.last_token.type == TOKEN.START_BLOCK:
876            if self._flags.inline_frame:
877                prefix = 'SPACE'
878            else:
879                prefix = 'NEWLINE'
880        elif self._flags.last_token.type == TOKEN.END_EXPR:
881            self._output.space_before_token = True
882            prefix = 'NEWLINE'
883
884        if reserved_array(current_token, Tokenizer.line_starters) and self._flags.last_token.text != ')':
885            if self._flags.inline_frame or self._flags.last_token.text == 'else ' or self._flags.last_token.text == 'export':
886                prefix = 'SPACE'
887            else:
888                prefix = 'NEWLINE'
889
890        if reserved_array(current_token, ['else', 'catch', 'finally']):
891            if ((not (self._flags.last_token.type == TOKEN.END_BLOCK and self._previous_flags.mode == MODE.BlockStatement))
892                or self._options.brace_style == 'expand'
893                or self._options.brace_style == 'end-expand'
894                or (self._options.brace_style == 'none' and current_token.newlines)) \
895               and not self._flags.inline_frame:
896                self.print_newline()
897            else:
898                self._output.trim(True)
899                # If we trimmed and there's something other than a close block before us
900                # put a newline back in.  Handles '} // comment' scenario.
901                if self._output.current_line.last() != '}':
902                    self.print_newline()
903
904                self._output.space_before_token = True
905
906        elif prefix == 'NEWLINE':
907            if reserved_array(self._flags.last_token, _special_word_set):
908                # no newline between return nnn
909                self._output.space_before_token = True
910            elif self._flags.last_token.text == 'declare' and reserved_array(current_token, [
911                    'var',
912                    'let',
913                    'const']):
914                # accomodates Typescript declare formatting
915                self._output.space_before_token = True
916            elif self._flags.last_token.type != TOKEN.END_EXPR:
917                if (
918                    self._flags.last_token.type != TOKEN.START_EXPR or not (
919                        reserved_array(current_token, [
920                            'var',
921                            'let',
922                            'const']))) and self._flags.last_token.text != ':':
923                    # no need to force newline on VAR -
924                    # for (var x = 0...
925                    if reserved_word(current_token, 'if') and self._flags.last_token.text == 'else':
926                        self._output.space_before_token = True
927                    else:
928                        self.print_newline()
929            elif reserved_array(current_token, Tokenizer.line_starters) and self._flags.last_token.text != ')':
930                self.print_newline()
931        elif self._flags.multiline_frame and self.is_array(self._flags.mode) and self._flags.last_token.text == ',' and self._last_last_text == '}':
932            self.print_newline()  # }, in lists get a newline
933        elif prefix == 'SPACE':
934            self._output.space_before_token = True
935
936        if current_token.previous and (current_token.previous.type == TOKEN.WORD or
937                    current_token.previous.type == TOKEN.RESERVED):
938            self._output.space_before_token = True
939
940        self.print_token(current_token)
941        self._flags.last_word = current_token.text
942
943        if current_token.type == TOKEN.RESERVED:
944            if current_token.text == 'do':
945                self._flags.do_block = True
946            elif current_token.text == 'if':
947                self._flags.if_block = True
948            elif current_token.text == 'import':
949                self._flags.import_block = True
950            elif current_token.text == 'from' and self._flags.import_block:
951                self._flags.import_block = False
952
953    def handle_semicolon(self, current_token):
954        if self.start_of_statement(current_token):
955            # The conditional starts the statement if appropriate.
956            # Semicolon can be the start (and end) of a statement
957            self._output.space_before_token = False
958        else:
959            self.handle_whitespace_and_comments(current_token)
960
961        next_token = self._tokens.peek()
962        while (self._flags.mode == MODE.Statement and
963                not (self._flags.if_block and reserved_word(next_token, 'else')) and
964                not self._flags.do_block):
965            self.restore_mode()
966
967        if self._flags.import_block:
968            self._flags.import_block = False
969
970        self.print_token(current_token)
971
972    def handle_string(self, current_token):
973        if self.start_of_statement(current_token):
974            # The conditional starts the statement if appropriate.
975            # One difference - strings want at least a space before
976            self._output.space_before_token = True
977        else:
978            self.handle_whitespace_and_comments(current_token)
979
980            if self._flags.last_token.type == TOKEN.RESERVED or self._flags.last_token.type == TOKEN.WORD or self._flags.inline_frame:
981                self._output.space_before_token = True
982            elif self._flags.last_token.type in [TOKEN.COMMA, TOKEN.START_EXPR, TOKEN.EQUALS, TOKEN.OPERATOR]:
983                if not self.start_of_object_property():
984                    self.allow_wrap_or_preserved_newline(current_token)
985            else:
986                self.print_newline()
987
988        self.print_token(current_token)
989
990    def handle_equals(self, current_token):
991        if self.start_of_statement(current_token):
992            # The conditional starts the statement if appropriate.
993            pass
994        else:
995            self.handle_whitespace_and_comments(current_token)
996
997        if self._flags.declaration_statement:
998            # just got an '=' in a var-line, different line breaking rules will
999            # apply
1000            self._flags.declaration_assignment = True
1001
1002        self._output.space_before_token = True
1003        self.print_token(current_token)
1004        self._output.space_before_token = True
1005
1006    def handle_comma(self, current_token):
1007        self.handle_whitespace_and_comments(current_token, True)
1008
1009        self.print_token(current_token)
1010        self._output.space_before_token = True
1011
1012        if self._flags.declaration_statement:
1013            if self.is_expression(self._flags.parent.mode):
1014                # do not break on comma, for ( var a = 1, b = 2
1015                self._flags.declaration_assignment = False
1016
1017            if self._flags.declaration_assignment:
1018                self._flags.declaration_assignment = False
1019                self.print_newline(preserve_statement_flags=True)
1020            elif self._options.comma_first:
1021                # for comma-first, we want to allow a newline before the comma
1022                # to turn into a newline after the comma, which we will fixup
1023                # later
1024                self.allow_wrap_or_preserved_newline(current_token)
1025
1026        elif self._flags.mode == MODE.ObjectLiteral \
1027                or (self._flags.mode == MODE.Statement and self._flags.parent.mode == MODE.ObjectLiteral):
1028            if self._flags.mode == MODE.Statement:
1029                self.restore_mode()
1030
1031            if not self._flags.inline_frame:
1032                self.print_newline()
1033        elif self._options.comma_first:
1034            # EXPR or DO_BLOCK
1035            # for comma-first, we want to allow a newline before the comma
1036            # to turn into a newline after the comma, which we will fixup later
1037            self.allow_wrap_or_preserved_newline(current_token)
1038
1039    def handle_operator(self, current_token):
1040        isGeneratorAsterisk = current_token.text == '*' and \
1041            (reserved_array(self._flags.last_token, ['function', 'yield']) or
1042                (self._flags.last_token.type in [TOKEN.START_BLOCK, TOKEN.COMMA, TOKEN.END_BLOCK, TOKEN.SEMICOLON]))
1043        isUnary = current_token.text in ['+', '-'] \
1044            and (self._flags.last_token.type in [TOKEN.START_BLOCK, TOKEN.START_EXPR, TOKEN.EQUALS, TOKEN.OPERATOR]
1045                 or self._flags.last_token.text in Tokenizer.line_starters or self._flags.last_token.text == ',')
1046
1047        if self.start_of_statement(current_token):
1048            # The conditional starts the statement if appropriate.
1049            pass
1050        else:
1051            preserve_statement_flags = not isGeneratorAsterisk
1052            self.handle_whitespace_and_comments(
1053                current_token, preserve_statement_flags)
1054
1055        if reserved_array(self._flags.last_token, _special_word_set):
1056            # return had a special handling in TK_WORD
1057            self._output.space_before_token = True
1058            self.print_token(current_token)
1059            return
1060
1061        # hack for actionscript's import .*;
1062        if current_token.text == '*' and self._flags.last_token.type == TOKEN.DOT:
1063            self.print_token(current_token)
1064            return
1065
1066        if current_token.text == '::':
1067            # no spaces around the exotic namespacing syntax operator
1068            self.print_token(current_token)
1069            return
1070
1071        # Allow line wrapping between operators when operator_position is
1072        #   set to before or preserve
1073        if self._flags.last_token.type == TOKEN.OPERATOR and self._options.operator_position in OPERATOR_POSITION_BEFORE_OR_PRESERVE:
1074            self.allow_wrap_or_preserved_newline(current_token)
1075
1076        if current_token.text == ':' and self._flags.in_case:
1077            self.print_token(current_token)
1078            self._flags.in_case = False
1079            self._flags.case_body = True
1080            if self._tokens.peek().type != TOKEN.START_BLOCK:
1081                self.indent()
1082                self.print_newline()
1083            else:
1084                self._output.space_before_token = True
1085
1086            return
1087
1088        space_before = True
1089        space_after = True
1090        in_ternary = False
1091
1092        if current_token.text == ':':
1093            if self._flags.ternary_depth == 0:
1094                # Colon is invalid javascript outside of ternary and object,
1095                # but do our best to guess what was meant.
1096                space_before = False
1097            else:
1098                self._flags.ternary_depth -= 1
1099                in_ternary = True
1100        elif current_token.text == '?':
1101            self._flags.ternary_depth += 1
1102
1103        # let's handle the operator_position option prior to any conflicting
1104        # logic
1105        if (not isUnary) and (not isGeneratorAsterisk) and \
1106                self._options.preserve_newlines and current_token.text in Tokenizer.positionable_operators:
1107
1108            isColon = current_token.text == ':'
1109            isTernaryColon = isColon and in_ternary
1110            isOtherColon = isColon and not in_ternary
1111
1112            if self._options.operator_position == OPERATOR_POSITION['before_newline']:
1113                # if the current token is : and it's not a ternary statement
1114                # then we set space_before to false
1115                self._output.space_before_token = not isOtherColon
1116
1117                self.print_token(current_token)
1118
1119                if (not isColon) or isTernaryColon:
1120                    self.allow_wrap_or_preserved_newline(current_token)
1121
1122                self._output.space_before_token = True
1123
1124                return
1125
1126            elif self._options.operator_position == OPERATOR_POSITION['after_newline']:
1127                # if the current token is anything but colon, or (via deduction) it's a colon and in a ternary statement,
1128                #   then print a newline.
1129                self._output.space_before_token = True
1130
1131                if (not isColon) or isTernaryColon:
1132                    if self._tokens.peek().newlines:
1133                        self.print_newline(preserve_statement_flags=True)
1134                    else:
1135                        self.allow_wrap_or_preserved_newline(current_token)
1136                else:
1137                    self._output.space_before_token = False
1138
1139                self.print_token(current_token)
1140
1141                self._output.space_before_token = True
1142                return
1143
1144            elif self._options.operator_position == OPERATOR_POSITION['preserve_newline']:
1145                if not isOtherColon:
1146                    self.allow_wrap_or_preserved_newline(current_token)
1147
1148                # if we just added a newline, or the current token is : and it's not a ternary statement,
1149                #   then we set space_before to false
1150                self._output.space_before_token = not (
1151                    self._output.just_added_newline() or isOtherColon)
1152
1153                self.print_token(current_token)
1154
1155                self._output.space_before_token = True
1156                return
1157
1158        if isGeneratorAsterisk:
1159            self.allow_wrap_or_preserved_newline(current_token)
1160            space_before = False
1161            next_token = self._tokens.peek()
1162            space_after = next_token and next_token.type in [
1163                TOKEN.WORD, TOKEN.RESERVED]
1164        elif current_token.text == '...':
1165            self.allow_wrap_or_preserved_newline(current_token)
1166            space_before = self._flags.last_token.type == TOKEN.START_BLOCK
1167            space_after = False
1168        elif current_token.text in ['--', '++', '!', '~'] or isUnary:
1169            if self._flags.last_token.type == TOKEN.COMMA or self._flags.last_token.type == TOKEN.START_EXPR:
1170                self.allow_wrap_or_preserved_newline(current_token)
1171
1172            space_before = False
1173            space_after = False
1174
1175            # http://www.ecma-international.org/ecma-262/5.1/#sec-7.9.1
1176            # if there is a newline between -- or ++ and anything else we
1177            # should preserve it.
1178            if current_token.newlines and (
1179                    current_token.text == '--' or current_token.text == '++'):
1180                self.print_newline(preserve_statement_flags=True)
1181
1182            if self._flags.last_token.text == ';' and self.is_expression(
1183                    self._flags.mode):
1184                # for (;; ++i)
1185                #         ^^
1186                space_before = True
1187
1188            if self._flags.last_token.type == TOKEN.RESERVED:
1189                space_before = True
1190            elif self._flags.last_token.type == TOKEN.END_EXPR:
1191                space_before = not (
1192                    self._flags.last_token.text == ']' and current_token.text in [
1193                        '--', '++'])
1194            elif self._flags.last_token.type == TOKEN.OPERATOR:
1195                # a++ + ++b
1196                # a - -b
1197                space_before = current_token.text in [
1198                    '--', '-', '++', '+'] and self._flags.last_token.text in ['--', '-', '++', '+']
1199                # + and - are not unary when preceeded by -- or ++ operator
1200                # a-- + b
1201                # a * +b
1202                # a - -b
1203                if current_token.text in [
1204                        '-', '+'] and self._flags.last_token.text in ['--', '++']:
1205                    space_after = True
1206
1207            if (((self._flags.mode == MODE.BlockStatement and not self._flags.inline_frame)
1208                 or self._flags.mode == MODE.Statement) and self._flags.last_token.text in ['{', ';']):
1209                # { foo: --i }
1210                # foo(): --bar
1211                self.print_newline()
1212
1213        if space_before:
1214            self._output.space_before_token = True
1215
1216        self.print_token(current_token)
1217
1218        if space_after:
1219            self._output.space_before_token = True
1220
1221    def handle_block_comment(self, current_token, preserve_statement_flags):
1222        if self._output.raw:
1223            self._output.add_raw_token(current_token)
1224            if current_token.directives and current_token.directives.get(
1225                    'preserve') == 'end':
1226                # If we're testing the raw output behavior, do not allow a
1227                # directive to turn it off.
1228                self._output.raw = self._options.test_output_raw
1229            return
1230
1231        if current_token.directives:
1232            self.print_newline(
1233                preserve_statement_flags=preserve_statement_flags)
1234            self.print_token(current_token)
1235            if current_token.directives.get('preserve') == 'start':
1236                self._output.raw = True
1237
1238            self.print_newline(preserve_statement_flags=True)
1239            return
1240
1241        # inline block
1242        if not self.acorn.newline.search(
1243                current_token.text) and not current_token.newlines:
1244            self._output.space_before_token = True
1245            self.print_token(current_token)
1246            self._output.space_before_token = True
1247            return
1248
1249        lines = self.acorn.allLineBreaks.split(current_token.text)
1250        javadoc = False
1251        starless = False
1252        last_indent = current_token.whitespace_before
1253        last_indent_length = len(last_indent)
1254
1255        # block comment starts with a new line
1256        self.print_newline(preserve_statement_flags=preserve_statement_flags)
1257
1258        # first line always indented
1259        self.print_token(current_token, lines[0])
1260        self.print_newline(preserve_statement_flags=preserve_statement_flags)
1261
1262        if len(lines) > 1:
1263            lines = lines[1:]
1264            javadoc = not any(l for l in lines if (
1265                l.strip() == '' or (l.lstrip())[0] != '*'))
1266            starless = all(l.startswith(last_indent)
1267                           or l.strip() == '' for l in lines)
1268
1269            if javadoc:
1270                self._flags.alignment = 1
1271
1272            for line in lines:
1273                if javadoc:
1274                    # javadoc: reformat and re-indent
1275                    self.print_token(current_token, line.lstrip())
1276                elif starless and len(line) > last_indent_length:
1277                    # starless: re-indent non-empty content, avoiding trim
1278                    self.print_token(current_token, line[last_indent_length:])
1279                else:
1280                    # normal comments output raw
1281                    self._output.current_line.set_indent(-1)
1282                    self._output.add_token(line)
1283
1284                # for comments on their own line or  more than one line,
1285                # make sure there's a new line after
1286                self.print_newline(preserve_statement_flags=preserve_statement_flags)
1287
1288            self._flags.alignment = 0
1289
1290    def handle_comment(self, current_token, preserve_statement_flags):
1291        if current_token.newlines:
1292            self.print_newline(
1293                preserve_statement_flags=preserve_statement_flags)
1294
1295        if not current_token.newlines:
1296            self._output.trim(True)
1297
1298        self._output.space_before_token = True
1299        self.print_token(current_token)
1300        self.print_newline(preserve_statement_flags=preserve_statement_flags)
1301
1302    def handle_dot(self, current_token):
1303        if self.start_of_statement(current_token):
1304            # The conditional starts the statement if appropriate.
1305            pass
1306        else:
1307            self.handle_whitespace_and_comments(current_token, True)
1308
1309        if reserved_array(self._flags.last_token, _special_word_set):
1310            self._output.space_before_token = False
1311        else:
1312            # allow preserved newlines before dots in general
1313            # force newlines on dots after close paren when break_chained - for
1314            # bar().baz()
1315            self.allow_wrap_or_preserved_newline(
1316                current_token, self._flags.last_token.text == ')' and
1317                    self._options.break_chained_methods)
1318
1319        # Only unindent chained method dot if this dot starts a new line.
1320        # Otherwise the automatic extra indentation removal
1321        # will handle any over indent
1322        if self._options.unindent_chained_methods and \
1323                self._output.just_added_newline():
1324            self.deindent()
1325
1326        self.print_token(current_token)
1327
1328    def handle_unknown(self, current_token, preserve_statement_flags):
1329        self.print_token(current_token)
1330        if current_token.text[-1] == '\n':
1331            self.print_newline(
1332                preserve_statement_flags=preserve_statement_flags)
1333
1334    def handle_eof(self, current_token):
1335        # Unwind any open statements
1336        while self._flags.mode == MODE.Statement:
1337            self.restore_mode()
1338
1339        self.handle_whitespace_and_comments(current_token)
1340