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