Permalink
Browse files

Synchronize js fixes and tweaks to python

  • Loading branch information...
1 parent 3f6ac6c commit c5789a8c7fd44099f0d3d1d175a6d41fb31cbcdd @bitwiseman bitwiseman committed Mar 19, 2013
Showing with 92 additions and 59 deletions.
  1. +52 −53 python/jsbeautifier/__init__.py
  2. +40 −6 python/jsbeautifier/tests/testjsbeautifier.py
@@ -77,12 +77,13 @@ def __repr__(self):
class BeautifierFlags:
def __init__(self, mode):
- self.previous_mode = 'BLOCK'
+ self.previous_mode = MODE.BlockStatement
self.mode = mode
self.var_line = False
self.var_line_tainted = False
self.var_line_reindented = False
self.in_html_comment = False
+ self.multiline_array = False
self.if_block = False
self.do_block = False
self.do_while = False
@@ -158,7 +159,9 @@ def usage():
-
+class MODE:
+ BlockStatement, Statement, ObjectLiteral, ArrayLiteral, \
+ ForInitializer, Conditional, Expression = range(7)
class Beautifier:
@@ -170,7 +173,7 @@ def __init__(self, opts = default_options() ):
def blank_state(self):
# internal flags
- self.flags = BeautifierFlags('BLOCK')
+ self.flags = BeautifierFlags(MODE.BlockStatement)
self.flag_store = []
self.wanted_newline = False
@@ -201,7 +204,8 @@ def blank_state(self):
# Words which always should start on a new line
self.line_starters = 'continue,try,throw,return,var,if,switch,case,default,for,while,break,function'.split(',')
- self.set_mode('BLOCK')
+
+ self.set_mode(MODE.BlockStatement)
self.parser_pos = 0
@@ -296,11 +300,11 @@ def is_special_word(self, s):
return s in ['case', 'return', 'do', 'if', 'throw', 'else']
def is_array(self, mode):
- return mode in ['[EXPRESSION]', '[INDENTED-EXPRESSION]']
+ return mode == MODE.ArrayLiteral
def is_expression(self, mode):
- return mode in ['[EXPRESSION]', '[INDENTED-EXPRESSION]', '(EXPRESSION)', '(FOR-EXPRESSION)', '(COND-EXPRESSION)']
+ return mode in [MODE.ArrayLiteral, MODE.Expression, MODE.ForInitializer, MODE.Conditional]
def just_added_newline(self):
return len(self.output) and self.output[-1] == '\n'
@@ -342,9 +346,13 @@ def append_newline(self, force_newline = False, preserve_statement_flags = False
if not preserve_statement_flags:
if self.last_text != ';':
- while self.flags.mode == 'STATEMENT' and not self.flags.if_block:
+ while self.flags.mode == MODE.Statement and not self.flags.if_block:
self.restore_mode();
+ if self.flags.mode == MODE.ArrayLiteral:
+ self.flags.multiline_array = True
+
+
if len(self.output) == 0:
# no newline on start of file
return
@@ -398,7 +406,7 @@ def indent(self):
def set_mode(self, mode):
- prev = BeautifierFlags('BLOCK')
+ prev = BeautifierFlags(MODE.BlockStatement)
if self.flags:
self.flag_store.append(self.flags)
@@ -423,9 +431,9 @@ def restore_mode(self):
def start_of_statement(self):
if (self.last_text == 'do' \
or (self.last_text == 'else' and self.token_text != 'if' ) \
- or (self.last_type == 'TK_END_EXPR' and (self.flags.previous_mode == '(FOR-EXPRESSION)' or self.flags.previous_mode == '(COND-EXPRESSION)'))):
+ or (self.last_type == 'TK_END_EXPR' and (self.flags.previous_mode == MODE.ForInitializer or self.flags.previous_mode == MODE.Conditional))):
self.allow_wrap_or_preserved_newline(self.token_text)
- self.set_mode('STATEMENT')
+ self.set_mode(MODE.Statement)
self.indent()
self.output_wrapped = False
return True
@@ -550,7 +558,7 @@ def get_next_token(self):
if c == "'" or c == '"' or \
(c == '/' and ((self.last_type == 'TK_WORD' and self.is_special_word(self.last_text)) or \
- (self.last_type == 'TK_END_EXPR' and self.flags.previous_mode in ['(FOR-EXPRESSION)', '(COND-EXPRESSION)']) or \
+ (self.last_type == 'TK_END_EXPR' and self.flags.previous_mode in [MODE.ForInitializer, MODE.Conditional]) or \
(self.last_type in ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK', 'TK_END_BLOCK', 'TK_OPERATOR',
'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON', 'TK_COMMA']))):
sep = c
@@ -700,40 +708,23 @@ def handle_start_expr(self, token_text):
if self.last_type == 'TK_WORD' or self.last_text == ')':
if self.last_text in self.line_starters:
self.output_space_before_token = True
- self.set_mode('(EXPRESSION)')
+ self.set_mode(MODE.Expression)
self.append_token(token_text)
return
- if self.flags.mode in ['[EXPRESSION]', '[INDENTED-EXPRESSION]']:
- if self.last_last_text == ']' and self.last_text == ',':
+ if self.is_array(self.flags.mode):
+ if self.last_text == '[' or (self.last_last_text == ']' and self.last_text == ','):
# ], [ goes to a new line
- if self.flags.mode == '[EXPRESSION]':
- self.flags.mode = '[INDENTED-EXPRESSION]'
- if not self.opts.keep_array_indentation:
- self.indent()
- self.set_mode('[EXPRESSION]')
if not self.opts.keep_array_indentation:
self.append_newline()
- elif self.last_text == '[':
- if self.flags.mode == '[EXPRESSION]':
- self.flags.mode = '[INDENTED-EXPRESSION]'
- if not self.opts.keep_array_indentation:
- self.indent()
- self.set_mode('[EXPRESSION]')
- if not self.opts.keep_array_indentation:
- self.append_newline()
- else:
- self.set_mode('[EXPRESSION]')
- else:
- self.set_mode('[EXPRESSION]')
else:
if self.last_text == 'for':
- self.set_mode('(FOR-EXPRESSION)')
+ self.set_mode(MODE.ForInitializer)
elif self.last_text in ['if', 'while']:
- self.set_mode('(COND-EXPRESSION)')
+ self.set_mode(MODE.Conditional)
else:
- self.set_mode('(EXPRESSION)')
+ self.set_mode(MODE.Expression)
if self.last_text == ';' or self.last_type == 'TK_START_BLOCK':
@@ -760,28 +751,32 @@ def handle_start_expr(self, token_text):
self.allow_wrap_or_preserved_newline(token_text)
self.append_token(token_text)
+ if self.token_text == '[':
+ self.set_mode(MODE.ArrayLiteral)
+ self.indent()
+
def handle_end_expr(self, token_text):
- if token_text == ']':
- if not self.opts.keep_array_indentation:
- if self.flags.mode == '[INDENTED-EXPRESSION]':
- if self.last_text == ']':
- self.restore_mode()
- self.append_newline()
- self.append_token(token_text)
- return
+ # statements inside expressions are not valid syntax, but...
+ # statements must all be closed when their container closes
+ while self.flags.mode == MODE.Statement:
+ self.restore_mode()
+
+ if self.token_text == ']' and self.is_array(self.flags.mode) and self.flags.multiline_array and not self.opts.keep_array_indentation:
+ self.append_newline()
+
self.restore_mode()
self.append_token(token_text)
# do {} while () // no statement required after
- if self.flags.do_while and self.flags.previous_mode == '(COND-EXPRESSION)':
- self.flags.previous_mode = '(EXPRESSION)'
+ if self.flags.do_while and self.flags.previous_mode == MODE.Conditional:
+ self.flags.previous_mode = MODE.Expression
self.flags.do_block = False
self.flags.do_while = False
def handle_start_block(self, token_text):
- self.set_mode('BLOCK')
+ self.set_mode(MODE.BlockStatement)
empty_braces = self.is_next('}')
if self.opts.brace_style == 'expand-strict':
@@ -812,6 +807,10 @@ def handle_start_block(self, token_text):
def handle_end_block(self, token_text):
+ # statements must all be closed when their container closes
+ while self.flags.mode == MODE.Statement:
+ self.restore_mode()
+
self.restore_mode()
if self.opts.brace_style == 'expand' or self.opts.brace_style == 'expand-strict':
if self.last_text != '{':
@@ -858,7 +857,7 @@ def handle_word(self, token_text):
# Need to unwind the modes correctly: if (a) if (b) c(); else d(); else e();
if self.flags.if_block:
if token_text != 'else':
- while self.flags.mode == 'STATEMENT':
+ while self.flags.mode == MODE.Statement:
self.restore_mode()
self.flags.if_block = False;
@@ -917,7 +916,7 @@ def handle_word(self, token_text):
else:
prefix = 'SPACE'
self.output_space_before_token = True
- elif self.last_type == 'TK_SEMICOLON' and self.flags.mode in 'BLOCK':
+ elif self.last_type == 'TK_SEMICOLON' and self.flags.mode == MODE.BlockStatement:
# TODO: Should this be for STATEMENT as well?
prefix = 'NEWLINE'
elif self.last_type == 'TK_SEMICOLON' and self.is_expression(self.flags.mode):
@@ -992,15 +991,15 @@ def handle_word(self, token_text):
def handle_semicolon(self, token_text):
- while self.flags.mode == 'STATEMENT' and not self.flags.if_block:
+ while self.flags.mode == MODE.Statement and not self.flags.if_block:
self.restore_mode()
self.append_token(token_text)
self.flags.var_line = False
self.flags.var_line_reindented = False
if self.flags.mode == 'OBJECT':
# OBJECT mode is weird and doesn't get reset too well.
- self.flags.mode = 'BLOCK'
+ self.flags.mode = MODE.BlockStatement
def handle_string(self, token_text):
@@ -1010,7 +1009,7 @@ def handle_string(self, token_text):
self.output_space_before_token = True
elif self.last_type == 'TK_WORD':
self.output_space_before_token = True
- elif self.last_type == 'TK_END_EXPR' and self.flags.previous_mode in ['(COND-EXPRESSION)', '(FOR-EXPRESSION)']:
+ elif self.last_type == 'TK_END_EXPR' and self.flags.previous_mode in [MODE.Conditional, MODE.ForInitializer]:
self.output_space_before_token = True
elif self.last_type in ['TK_COMMA', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']:
if self.flags.mode != 'OBJECT':
@@ -1050,7 +1049,7 @@ def handle_comma(self, token_text):
return
- if self.last_type == 'TK_END_BLOCK' and self.flags.mode != '(EXPRESSION)':
+ if self.last_type == 'TK_END_BLOCK' and self.flags.mode != MODE.Expression:
self.append_token(token_text)
if self.flags.mode == 'OBJECT' and self.last_text == '}':
self.append_newline()
@@ -1112,14 +1111,14 @@ def handle_operator(self, token_text):
if self.last_type == 'TK_WORD' and self.last_text in self.line_starters:
space_before = True
- if self.flags.mode == 'BLOCK' and self.last_text in ['{', ';']:
+ if self.flags.mode == MODE.BlockStatement and self.last_text in ['{', ';']:
# { foo: --i }
# foo(): --bar
self.append_newline()
elif token_text == ':':
if self.flags.ternary_depth == 0:
- if self.flags.mode == 'BLOCK':
+ if self.flags.mode == MODE.BlockStatement:
self.flags.mode = 'OBJECT'
space_before = False
else:
@@ -129,7 +129,13 @@ def test_beautifier(self):
bt("a = 1;\n // comment", "a = 1;\n// comment");
bt('a = [-1, -1, -1]');
- bt('o = [{a:b},{c:d}]', 'o = [{\n a: b\n}, {\n c: d\n}]');
+ # The exact formatting these should have is open for discussion, but they are at least reasonable
+ bt('a = [ // comment\n -1, -1, -1\n]');
+ bt('var a = [ // comment\n -1, -1, -1\n]');
+ bt('a = [ // comment\n -1, // comment\n -1, -1\n]');
+ bt('var a = [ // comment\n -1, // comment\n -1, -1\n]');
+
+ bt('o = [{a:b},{c:d}]', 'o = [{\n a: b\n }, {\n c: d\n }\n]');
bt("if (a) {\n do();\n}"); # was: extra space appended
@@ -183,7 +189,7 @@ def test_beautifier(self):
test_fragment('/incomplete-regex');
test_fragment('{a:1},{a:2}', '{\n a: 1\n}, {\n a: 2\n}');
- test_fragment('var ary=[{a:1}, {a:2}];', 'var ary = [{\n a: 1\n}, {\n a: 2\n}];');
+ test_fragment('var ary=[{a:1}, {a:2}];', 'var ary = [{\n a: 1\n }, {\n a: 2\n }\n];');
test_fragment('{a:#1', '{\n a: #1'); # incomplete
test_fragment('{a:#', '{\n a: #'); # incomplete
@@ -295,7 +301,7 @@ def test_beautifier(self):
bt("var a2, b2, c2, d2 = 0, c = function() {},\nd = '';", "var a2, b2, c2, d2 = 0,\n c = function() {},\n d = '';");
bt('var o2=$.extend(a);function(){alert(x);}', 'var o2 = $.extend(a);\n\nfunction() {\n alert(x);\n}');
- bt('{"x":[{"a":1,"b":3},7,8,8,8,8,{"b":99},{"a":11}]}', '{\n "x": [{\n "a": 1,\n "b": 3\n },\n 7, 8, 8, 8, 8, {\n "b": 99\n }, {\n "a": 11\n }]\n}');
+ bt('{"x":[{"a":1,"b":3},7,8,8,8,8,{"b":99},{"a":11}]}', '{\n "x": [{\n "a": 1,\n "b": 3\n },\n 7, 8, 8, 8, 8, {\n "b": 99\n }, {\n "a": 11\n }\n ]\n}');
bt('{"1":{"1a":"1b"},"2"}', '{\n "1": {\n "1a": "1b"\n },\n "2"\n}');
bt('{a:{a:b},c}', '{\n a: {\n a: b\n },\n c\n}');
@@ -353,12 +359,12 @@ def test_beautifier(self):
bt('var x = [{}\n]', 'var x = [{}\n]');
- bt('var x = [{foo:bar}\n]', 'var x = [{\n foo: bar\n}\n]');
- bt("a = ['something',\n'completely',\n'different'];\nif (x);");
+ bt('var x = [{foo:bar}\n]', 'var x = [{\n foo: bar\n }\n]');
+ bt("a = ['something',\n 'completely',\n 'different'];\nif (x);");
bt("a = ['a','b','c']", "a = ['a', 'b', 'c']");
bt("a = ['a', 'b','c']", "a = ['a', 'b', 'c']");
- bt("x = [{'a':0}]", "x = [{\n 'a': 0\n}]");
+ bt("x = [{'a':0}]", "x = [{\n 'a': 0\n }]");
bt('{a([[a1]], {b;});}', '{\n a([[a1]], {\n b;\n });\n}');
@@ -673,6 +679,20 @@ def test_beautifier(self):
bt('if (foo) if (bar) if (baz) whee();\na();');
bt('if (foo) a()\nif (bar) if (baz) whee();\na();');
+ bt('if (options)\n' +
+ ' for (var p in options)\n' +
+ ' this[p] = options[p];',
+ 'if (options) for (var p in options) this[p] = options[p];');
+
+ bt('function f(a,b) {if(a) b()}function g(a,b) {if(!a) b()}',
+ 'function f(a, b) {\n if (a) b()\n}\nfunction g(a, b) {\n if (!a) b()\n}');
+ bt('function f(a,b) {if(a) b()}\n\n\n\nfunction g(a,b) {if(!a) b()}',
+ 'function f(a, b) {\n if (a) b()\n}\n\nfunction g(a, b) {\n if (!a) b()\n}');
+ # This is not valid syntax, but still want to behave reasonably and not side-effect
+ bt('(if(a) b())(if(a) b())',
+ '(\nif (a) b())(\nif (a) b())');
+ bt('(if(a) b())\n\n\n(if(a) b())',
+ '(\nif (a) b())\n(\nif (a) b())');
bt("if\n(a)\nb();", "if (a) b();");
bt('var a =\nfoo', 'var a = foo');
@@ -700,6 +720,20 @@ def test_beautifier(self):
bt('if (foo) if (bar) if (baz) whee();\na();');
bt('if (foo) a()\nif (bar) if (baz) whee();\na();');
+ bt('if (options)\n' +
+ ' for (var p in options)\n' +
+ ' this[p] = options[p];');
+
+ bt('function f(a,b) {if(a) b()}function g(a,b) {if(!a) b()}',
+ 'function f(a, b) {\n if (a) b()\n}\nfunction g(a, b) {\n if (!a) b()\n}');
+ bt('function f(a,b) {if(a) b()}\n\n\n\nfunction g(a,b) {if(!a) b()}',
+ 'function f(a, b) {\n if (a) b()\n}\n\n\n\nfunction g(a, b) {\n if (!a) b()\n}');
+ # This is not valid syntax, but still want to behave reasonably and not side-effect
+ bt('(if(a) b())(if(a) b())',
+ '(\nif (a) b())(\nif (a) b())');
+ bt('(if(a) b())\n\n\n(if(a) b())',
+ '(\nif (a) b())\n\n\n(\nif (a) b())');
+
bt("if\n(a)\nb();", "if (a)\n b();");
bt('var a =\nfoo', 'var a =\n foo');

0 comments on commit c5789a8

Please sign in to comment.