From f7602b2d254a6562e223b96ce78d225220253f89 Mon Sep 17 00:00:00 2001 From: Liam Newman Date: Thu, 27 Mar 2014 18:12:31 -0700 Subject: [PATCH] Support for keywords as IdentifierNames such as .catch() This supports calling libraries which declare properties of objects using reserved words. This does not support declaring these fields. `var a = { 'throw': function() {} }` - works `a.throw()` - works `var a = { throw: function() {} }` - does not work Fixes #309 Fixes #351 Fixes #368 Fixes #378 --- js/lib/beautify.js | 82 +++++++++-------- js/test/beautify-tests.js | 6 ++ python/jsbeautifier/__init__.py | 92 ++++++++++--------- python/jsbeautifier/tests/testjsbeautifier.py | 6 ++ 4 files changed, 104 insertions(+), 82 deletions(-) diff --git a/js/lib/beautify.js b/js/lib/beautify.js index 8adfd04a7..74b38b0c1 100644 --- a/js/lib/beautify.js +++ b/js/lib/beautify.js @@ -86,7 +86,7 @@ var input, output_lines; var token_text, token_type, last_type, last_last_text, indent_string; var flags, previous_flags, flag_store; - var whitespace, wordchar, punct, parser_pos, line_starters, digits; + var whitespace, wordchar, punct, parser_pos, line_starters, reserved_words, digits; var prefix; var input_wanted_newline; var output_wrapped, output_space_before_token; @@ -104,6 +104,8 @@ // words which should always start on new line. line_starters = 'continue,try,throw,return,var,let,const,if,switch,case,default,for,while,break,function'.split(','); + reserved_words = line_starters.concat(['do', 'in', 'else', 'get', 'set', 'new', 'catch', 'finally', 'typeof']); + MODE = { BlockStatement: 'BlockStatement', // 'BLOCK' @@ -121,6 +123,7 @@ 'TK_START_BLOCK': handle_start_block, 'TK_END_BLOCK': handle_end_block, 'TK_WORD': handle_word, + 'TK_RESERVED': handle_word, 'TK_SEMICOLON': handle_semicolon, 'TK_STRING': handle_string, 'TK_EQUALS': handle_equals, @@ -553,14 +556,14 @@ function start_of_statement() { if ( - (flags.last_text === 'do' || - (flags.last_text === 'else' && token_text !== 'if') || + ((last_type === 'TK_RESERVED' && flags.last_text === 'do') || + (last_type === 'TK_RESERVED' && flags.last_text === 'else' && !(token_type === 'TK_RESERVED' && token_text === 'if')) || (last_type === 'TK_END_EXPR' && (previous_flags.mode === MODE.ForInitializer || previous_flags.mode === MODE.Conditional)))) { // Issue #276: // If starting a new statement with [if, for, while, do], push to a new line. // if (a) if (b) if(c) d(); else e(); else f(); allow_wrap_or_preserved_newline( - in_array(token_text, ['do', 'for', 'if', 'while'])); + token_type === 'TK_RESERVED' && in_array(token_text, ['do', 'for', 'if', 'while'])); set_mode(MODE.Statement); // Issue #275: @@ -733,8 +736,11 @@ return [c, 'TK_WORD']; } - if (c === 'in') { // hack for 'in' operator - return [c, 'TK_OPERATOR']; + if (last_type !== 'TK_DOT' && in_array(c, reserved_words)) { + if (c === 'in') { // hack for 'in' operator + return [c, 'TK_OPERATOR']; + } + return [c, 'TK_RESERVED']; } return [c, 'TK_WORD']; } @@ -806,7 +812,7 @@ (c === '/') || // regexp (opt.e4x && c === "<" && input.slice(parser_pos - 1).match(/^<([-a-zA-Z:0-9_.]+|{[^{}]*}|!\[CDATA\[[\s\S]*?\]\])\s*([-a-zA-Z:0-9_.]+=('[^']*'|"[^"]*"|{[^{}]*})\s*)*\/?\s*>/)) // xml ) && ( // regex and xml can only appear in specific locations during parsing - (last_type === 'TK_WORD' && is_special_word(flags.last_text)) || + (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) || (last_type === 'TK_END_EXPR' && in_array(previous_flags.mode, [MODE.Conditional, MODE.ForInitializer])) || (in_array(last_type, ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK', 'TK_END_BLOCK', 'TK_OPERATOR', 'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON', 'TK_COMMA' @@ -1006,10 +1012,10 @@ var next_mode = MODE.Expression; if (token_text === '[') { - if (last_type === 'TK_WORD' || flags.last_text === ')') { + if (last_type === 'TK_WORD' || last_type === 'TK_RESERVED' || flags.last_text === ')') { // this is array index specifier, break immediately // a[x], fn()[x] - if (in_array(flags.last_text, line_starters)) { + if (last_type === 'TK_RESERVED' && in_array(flags.last_text, line_starters)) { output_space_before_token = true; } set_mode(next_mode); @@ -1034,9 +1040,9 @@ } } else { - if (flags.last_text === 'for') { + if (last_type === 'TK_RESERVED' && flags.last_text === 'for') { next_mode = MODE.ForInitializer; - } else if (in_array(flags.last_text, ['if', 'while'])) { + } else if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['if', 'while'])) { next_mode = MODE.Conditional; } else { // next_mode = MODE.Expression; @@ -1050,14 +1056,14 @@ allow_wrap_or_preserved_newline(input_wanted_newline); output_wrapped = false; // do nothing on (( and )( and ][ and ]( and .( - } else if (last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') { + } else if (last_type !== 'TK_RESERVED' && last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') { output_space_before_token = true; - } else if (flags.last_word === 'function' || flags.last_word === 'typeof') { + } else if (last_type === 'TK_RESERVED' && (flags.last_word === 'function' || flags.last_word === 'typeof')) { // function() vs function () if (opt.jslint_happy) { output_space_before_token = true; } - } else if (in_array(flags.last_text, line_starters) || flags.last_text === 'catch') { + } else if (last_type === 'TK_RESERVED' && (in_array(flags.last_text, line_starters) || flags.last_text === 'catch')) { if (opt.space_before_conditional) { output_space_before_token = true; } @@ -1136,7 +1142,7 @@ if (last_type !== 'TK_OPERATOR' && (empty_anonymous_function || last_type === 'TK_EQUALS' || - (is_special_word(flags.last_text) && flags.last_text !== 'else'))) { + (last_type === 'TK_RESERVED' && is_special_word(flags.last_text) && flags.last_text !== 'else'))) { output_space_before_token = true; } else { print_newline(); @@ -1199,13 +1205,13 @@ } else if (input_wanted_newline && !is_expression(flags.mode) && (last_type !== 'TK_OPERATOR' || (flags.last_text === '--' || flags.last_text === '++')) && last_type !== 'TK_EQUALS' && - (opt.preserve_newlines || !in_array(flags.last_text, ['var', 'let', 'const']))) { + (opt.preserve_newlines || !(last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const'])))) { print_newline(); } if (flags.do_block && !flags.do_while) { - if (token_text === 'while') { + if (token_type === 'TK_RESERVED' && token_text === 'while') { // do {} ## while () output_space_before_token = true; print_token(); @@ -1224,7 +1230,7 @@ // Bare/inline ifs are tricky // Need to unwind the modes correctly: if (a) if (b) c(); else d(); else e(); if (flags.if_block) { - if (token_text !== 'else') { + if (!(token_type === 'TK_RESERVED' && token_text === 'else')) { while (flags.mode === MODE.Statement) { restore_mode(); } @@ -1232,7 +1238,7 @@ } } - if (token_text === 'case' || (token_text === 'default' && flags.in_case_statement)) { + if (token_type === 'TK_RESERVED' && (token_text === 'case' || (token_text === 'default' && flags.in_case_statement))) { print_newline(); if (flags.case_body || opt.jslint_happy) { // switch cases following one another @@ -1245,7 +1251,7 @@ return; } - if (token_text === 'function') { + if (token_type === 'TK_RESERVED' && token_text === 'function') { if (flags.var_line && last_type !== 'TK_EQUALS') { flags.var_line_reindented = true; } @@ -1257,8 +1263,8 @@ print_newline(true); } } - if (last_type === 'TK_WORD') { - if (flags.last_text === 'get' || flags.last_text === 'set' || flags.last_text === 'new' || flags.last_text === 'return') { + if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD') { + if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set', 'new', 'return'])) { output_space_before_token = true; } else { print_newline(); @@ -1279,7 +1285,7 @@ } } - if (token_text === 'function') { + if (token_type === 'TK_RESERVED' && token_text === 'function') { print_token(); flags.last_word = token_text; return; @@ -1288,7 +1294,7 @@ prefix = 'NONE'; if (last_type === 'TK_END_BLOCK') { - if (!in_array(token_text, ['else', 'catch', 'finally'])) { + if (!(token_type === 'TK_RESERVED' && in_array(token_text, ['else', 'catch', 'finally']))) { prefix = 'NEWLINE'; } else { if (opt.brace_style === "expand" || opt.brace_style === "end-expand") { @@ -1305,7 +1311,7 @@ prefix = 'SPACE'; } else if (last_type === 'TK_STRING') { prefix = 'NEWLINE'; - } else if (last_type === 'TK_WORD') { + } else if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD') { prefix = 'SPACE'; } else if (last_type === 'TK_START_BLOCK') { prefix = 'NEWLINE'; @@ -1314,7 +1320,7 @@ prefix = 'NEWLINE'; } - if (in_array(token_text, line_starters) && flags.last_text !== ')') { + if (token_type === 'TK_RESERVED' && in_array(token_text, line_starters) && flags.last_text !== ')') { if (flags.last_text === 'else') { prefix = 'SPACE'; } else { @@ -1323,7 +1329,7 @@ } - if (in_array(token_text, ['else', 'catch', 'finally'])) { + if (token_type === 'TK_RESERVED' && in_array(token_text, ['else', 'catch', 'finally'])) { if (last_type !== 'TK_END_BLOCK' || opt.brace_style === "expand" || opt.brace_style === "end-expand") { print_newline(); } else { @@ -1337,13 +1343,13 @@ output_space_before_token = true; } } else if (prefix === 'NEWLINE') { - if (is_special_word(flags.last_text)) { + if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) { // no newline between 'return nnn' output_space_before_token = true; } else if (last_type !== 'TK_END_EXPR') { - if ((last_type !== 'TK_START_EXPR' || !in_array(token_text, ['var', 'let', 'const'])) && flags.last_text !== ':') { + if ((last_type !== 'TK_START_EXPR' || !(token_type === 'TK_RESERVED' && in_array(token_text, ['var', 'let', 'const']))) && flags.last_text !== ':') { // no need to force newline on 'var': for (var x = 0...) - if (token_text === 'if' && flags.last_word === 'else' && flags.last_text !== '{') { + if (token_type === 'TK_RESERVED' && token_text === 'if' && flags.last_word === 'else' && flags.last_text !== '{') { // no newline for } else if { output_space_before_token = true; } else { @@ -1352,7 +1358,7 @@ print_newline(); } } - } else if (in_array(token_text, line_starters) && flags.last_text !== ')') { + } else if (token_type === 'TK_RESERVED' && in_array(token_text, line_starters) && flags.last_text !== ')') { flags.var_line = false; flags.var_line_reindented = false; print_newline(); @@ -1365,17 +1371,17 @@ print_token(); flags.last_word = token_text; - if (in_array(token_text, ['var', 'let', 'const'])) { + if (token_type === 'TK_RESERVED' && in_array(token_text, ['var', 'let', 'const'])) { flags.var_line = true; flags.var_line_reindented = false; flags.var_line_tainted = false; } - if (token_text === 'do') { + if (token_type === 'TK_RESERVED' && token_text === 'do') { flags.do_block = true; } - if (token_text === 'if') { + if (token_type === 'TK_RESERVED' && token_text === 'if') { flags.if_block = true; } } @@ -1404,7 +1410,7 @@ // The conditional starts the statement if appropriate. // One difference - strings want at least a space before output_space_before_token = true; - } else if (last_type === 'TK_WORD') { + } else if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD') { output_space_before_token = true; } else if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') { if (!start_of_object_property()) { @@ -1470,7 +1476,7 @@ function handle_operator() { var space_before = true; var space_after = true; - if (is_special_word(flags.last_text)) { + if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) { // "return" had a special handling in TK_WORD. Now we need to return the favor output_space_before_token = true; print_token(); @@ -1516,7 +1522,7 @@ space_before = true; } - if (last_type === 'TK_WORD' && in_array(flags.last_text, line_starters)) { + if (last_type === 'TK_RESERVED') { space_before = true; } @@ -1591,7 +1597,7 @@ } function handle_dot() { - if (is_special_word(flags.last_text)) { + if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) { output_space_before_token = true; } else { // allow preserved newlines before dots in general diff --git a/js/test/beautify-tests.js b/js/test/beautify-tests.js index 8a878b46a..2faa3c696 100755 --- a/js/test/beautify-tests.js +++ b/js/test/beautify-tests.js @@ -280,6 +280,12 @@ function run_beautifier_tests(test_obj, Urlencoded, js_beautify, html_beautify, // a common snippet in jQuery plugins bt("settings = $.extend({},defaults,settings);", "settings = $.extend({}, defaults, settings);"); + // reserved words used as property names + bt("$http().then().finally().default()", "$http().then().finally().default()"); + bt("$http()\n.then()\n.finally()\n.default()", "$http()\n .then()\n .finally()\n .default()"); + bt("$http().when.in.new.catch().throw()", "$http().when.in.new.catch().throw()"); + bt("$http()\n.when\n.in\n.new\n.catch()\n.throw()", "$http()\n .when\n .in\n .new\n .catch()\n .throw()"); + bt('{xxx;}()', '{\n xxx;\n}()'); bt("a = 'a'\nb = 'b'"); diff --git a/python/jsbeautifier/__init__.py b/python/jsbeautifier/__init__.py index 6f0dd30ea..579062fd1 100644 --- a/python/jsbeautifier/__init__.py +++ b/python/jsbeautifier/__init__.py @@ -265,6 +265,7 @@ def blank_state(self): # Words which always should start on a new line self.line_starters = 'continue,try,throw,return,var,let,const,if,switch,case,default,for,while,break,function'.split(',') + self.reserved_words = self.line_starters + ['do', 'in', 'else', 'get', 'set', 'new', 'catch', 'finally', 'typeof']; self.set_mode(MODE.BlockStatement) @@ -294,6 +295,7 @@ def beautify(self, s, opts = None ): 'TK_START_BLOCK': self.handle_start_block, 'TK_END_BLOCK': self.handle_end_block, 'TK_WORD': self.handle_word, + 'TK_RESERVED': self.handle_word, 'TK_SEMICOLON': self.handle_semicolon, 'TK_STRING': self.handle_string, 'TK_EQUALS': self.handle_equals, @@ -307,10 +309,10 @@ def beautify(self, s, opts = None ): } while True: - self.token_text, token_type = self.get_next_token() + self.token_text, self.token_type = self.get_next_token() - #print (token_text, token_type, self.flags.mode) - if token_type == 'TK_EOF': + #print (token_text, self.token_type, self.flags.mode) + if self.token_type == 'TK_EOF': break keep_whitespace = self.opts.keep_array_indentation and self.is_array(self.flags.mode) @@ -328,15 +330,15 @@ def beautify(self, s, opts = None ): for i in range(self.n_newlines): self.append_newline(i != 0) - handlers[token_type](self.token_text) + handlers[self.token_type](self.token_text) # The cleanest handling of inline comments is to treat them as though they aren't there. # Just continue formatting and the behavior should be logical. - if token_type != 'TK_INLINE_COMMENT' and token_type != 'TK_COMMENT' and token_type != 'TK_BLOCK_COMMENT' and token_type != 'TK_UNKNOWN': + if self.token_type != 'TK_INLINE_COMMENT' and self.token_type != 'TK_COMMENT' and self.token_type != 'TK_BLOCK_COMMENT' and self.token_type != 'TK_UNKNOWN': self.last_last_text = self.flags.last_text - self.last_type = token_type + self.last_type = self.token_type self.flags.last_text = self.token_text - self.flags.had_comment = token_type in ['TK_COMMENT', 'TK_INLINE_COMMENT', 'TK_BLOCK_COMMENT'] + self.flags.had_comment = self.token_type in ['TK_COMMENT', 'TK_INLINE_COMMENT', 'TK_BLOCK_COMMENT'] sweet_code = ''.join(self.output_lines[0].text) if len(self.output_lines) > 1: @@ -542,13 +544,13 @@ def start_of_object_property(self): self.flags.ternary_depth == 0 def start_of_statement(self): - if (self.flags.last_text == 'do' \ - or (self.flags.last_text == 'else' and self.token_text != 'if' ) \ + if ((self.last_type == 'TK_RESERVED' and self.flags.last_text == 'do') \ + or (self.last_type == 'TK_RESERVED' and self.flags.last_text == 'else' and not (self.token_type == 'TK_RESERVED' and self.token_text == 'if' )) \ or (self.last_type == 'TK_END_EXPR' and (self.previous_flags.mode == MODE.ForInitializer or self.previous_flags.mode == MODE.Conditional))): # Issue #276: # If starting a new statement with [if, for, while, do], push to a new line. # if (a) if (b) if(c) d(); else e(); else f(); - self.allow_wrap_or_preserved_newline(self.token_text, self.token_text in ['do', 'for', 'if', 'while']); + self.allow_wrap_or_preserved_newline(self.token_text, self.token_type == 'TK_RESERVED' and self.token_text in ['do', 'for', 'if', 'while']); self.set_mode(MODE.Statement); # Issue #275: @@ -622,9 +624,11 @@ def get_next_token(self): t = self.get_next_token() c += sign + t[0] return c, 'TK_WORD' + if self.last_type != 'TK_DOT' and c in self.reserved_words: + if c == 'in': # in is an operator, need to hack + return c, 'TK_OPERATOR' - if c == 'in': # in is an operator, need to hack - return c, 'TK_OPERATOR' + return c, 'TK_RESERVED' return c, 'TK_WORD' @@ -681,7 +685,7 @@ def get_next_token(self): (c == '/') or \ (self.opts.e4x and c == "<" and re.match('^<(!\[CDATA\[[\s\S]*?\]\]|[-a-zA-Z:0-9_.]+|\{[^{}]*\})\s*([-a-zA-Z:0-9_.]+=(\{[^{}]*\}|"[^"]*"|\'[^\']*\')\s*)*\/?\s*>', self.input[self.parser_pos - 1:])) \ ) and ( \ - (self.last_type == 'TK_WORD' and self.is_special_word(self.flags.last_text)) or \ + (self.last_type == 'TK_RESERVED' and self.is_special_word(self.flags.last_text)) or \ (self.last_type == 'TK_END_EXPR' and self.previous_flags.mode in [MODE.Conditional, MODE.ForInitializer]) 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'])): @@ -864,8 +868,8 @@ def handle_start_expr(self, token_text): next_mode = MODE.Expression if token_text == '[': - if self.last_type == 'TK_WORD' or self.flags.last_text == ')': - if self.flags.last_text in self.line_starters: + if self.last_type == 'TK_WORD' or self.last_type == 'TK_RESERVED' or self.flags.last_text == ')': + if self.last_type == 'TK_RESERVED' and self.flags.last_text in self.line_starters: self.output_space_before_token = True self.set_mode(next_mode) self.append_token(token_text) @@ -885,9 +889,9 @@ def handle_start_expr(self, token_text): self.append_newline() else: - if self.flags.last_text == 'for': + if self.last_type == 'TK_RESERVED' and self.flags.last_text == 'for': next_mode = MODE.ForInitializer - elif self.flags.last_text in ['if', 'while']: + elif self.last_type == 'TK_RESERVED' and self.flags.last_text in ['if', 'while']: next_mode = MODE.Conditional else: next_mode = MODE.Expression @@ -901,13 +905,13 @@ def handle_start_expr(self, token_text): self.allow_wrap_or_preserved_newline(token_text, self.input_wanted_newline); self.output_wrapped = False; - elif self.last_type not in ['TK_WORD', 'TK_OPERATOR']: + elif self.last_type not in ['TK_RESERVED', 'TK_WORD', 'TK_OPERATOR']: self.output_space_before_token = True - elif self.flags.last_word == 'function' or self.flags.last_word == 'typeof': + elif self.last_type == 'TK_RESERVED' and (self.flags.last_word == 'function' or self.flags.last_word == 'typeof'): # function() vs function (), typeof() vs typeof () if self.opts.jslint_happy: self.output_space_before_token = True - elif self.flags.last_text in self.line_starters or self.flags.last_text == 'catch': + elif self.last_type == 'TK_RESERVED' and (self.flags.last_text in self.line_starters or self.flags.last_text == 'catch'): # TODO: option space_before_conditional self.output_space_before_token = True @@ -975,7 +979,7 @@ def handle_start_block(self, token_text): if self.last_type != 'TK_OPERATOR' and \ (empty_anonymous_function or self.last_type == 'TK_EQUALS' or - (self.is_special_word(self.flags.last_text) and self.flags.last_text != 'else')): + (self.last_type == 'TK_RESERVED' and self.is_special_word(self.flags.last_text) and self.flags.last_text != 'else')): self.output_space_before_token = True else: self.append_newline() @@ -1028,11 +1032,11 @@ def handle_word(self, token_text): not self.is_expression(self.flags.mode) and \ (self.last_type != 'TK_OPERATOR' or (self.flags.last_text == '--' or self.flags.last_text == '++')) and \ self.last_type != 'TK_EQUALS' and \ - (self.opts.preserve_newlines or self.flags.last_text not in ['var', 'let', 'const']): + (self.opts.preserve_newlines or not (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['var', 'let', 'const'])): self.append_newline() if self.flags.do_block and not self.flags.do_while: - if token_text == 'while': + if self.token_type == 'TK_RESERVED' and token_text == 'while': # do {} ## while () self.output_space_before_token = True self.append_token(token_text) @@ -1049,13 +1053,13 @@ def handle_word(self, token_text): # Bare/inline ifs are tricky # Need to unwind the modes correctly: if (a) if (b) c(); else d(); else e(); if self.flags.if_block: - if token_text != 'else': + if not (self.token_type == 'TK_RESERVED' and token_text == 'else'): while self.flags.mode == MODE.Statement: self.restore_mode() self.flags.if_block = False; - if token_text == 'case' or (token_text == 'default' and self.flags.in_case_statement): + if self.token_type == 'TK_RESERVED' and (token_text == 'case' or (token_text == 'default' and self.flags.in_case_statement)): self.append_newline() if self.flags.case_body or self.opts.jslint_happy: self.flags.case_body = False @@ -1065,7 +1069,7 @@ def handle_word(self, token_text): self.flags.in_case_statement = True return - if token_text == 'function': + if self.token_type == 'TK_RESERVED' and token_text == 'function': if self.flags.var_line and self.flags.last_text != '=': self.flags.var_line_reindented = not self.opts.keep_function_indentation @@ -1076,8 +1080,8 @@ def handle_word(self, token_text): self.append_newline() self.append_newline(True) - if self.last_type == 'TK_WORD': - if self.flags.last_text in ['get', 'set', 'new', 'return']: + if self.last_type == 'TK_RESERVED' or self.last_type == 'TK_WORD': + if self.last_type == 'TK_RESERVED' and self.flags.last_text in ['get', 'set', 'new', 'return']: self.output_space_before_token = True else: self.append_newline() @@ -1094,7 +1098,7 @@ def handle_word(self, token_text): if not self.start_of_object_property(): self.allow_wrap_or_preserved_newline(token_text) - if token_text == 'function': + if self.token_type == 'TK_RESERVED' and token_text == 'function': self.append_token(token_text) self.flags.last_word = token_text return @@ -1102,7 +1106,7 @@ def handle_word(self, token_text): prefix = 'NONE' if self.last_type == 'TK_END_BLOCK': - if token_text not in ['else', 'catch', 'finally']: + if not (self.token_type == 'TK_RESERVED' and token_text in ['else', 'catch', 'finally']): prefix = 'NEWLINE' else: if self.opts.brace_style in ['expand', 'end-expand']: @@ -1117,7 +1121,7 @@ def handle_word(self, token_text): prefix = 'SPACE' elif self.last_type == 'TK_STRING': prefix = 'NEWLINE' - elif self.last_type == 'TK_WORD': + elif self.last_type == 'TK_RESERVED' or self.last_type == 'TK_WORD': prefix = 'SPACE' elif self.last_type == 'TK_START_BLOCK': prefix = 'NEWLINE' @@ -1125,13 +1129,13 @@ def handle_word(self, token_text): self.output_space_before_token = True prefix = 'NEWLINE' - if token_text in self.line_starters: + if self.token_type == 'TK_RESERVED' and token_text in self.line_starters and self.flags.last_text != ')': if self.flags.last_text == 'else': prefix = 'SPACE' else: prefix = 'NEWLINE' - if token_text in ['else', 'catch', 'finally']: + if self.token_type == 'TK_RESERVED' and token_text in ['else', 'catch', 'finally']: if self.last_type != 'TK_END_BLOCK' \ or self.opts.brace_style == 'expand' \ or self.opts.brace_style == 'end-expand': @@ -1147,20 +1151,20 @@ def handle_word(self, token_text): self.output_space_before_token = True elif prefix == 'NEWLINE': - if self.is_special_word(self.flags.last_text): + if self.last_type == 'TK_RESERVED' and self.is_special_word(self.flags.last_text): # no newline between return nnn self.output_space_before_token = True elif self.last_type != 'TK_END_EXPR': - if (self.last_type != 'TK_START_EXPR' or token_text not in ['var', 'let', 'const']) and self.flags.last_text != ':': + if (self.last_type != 'TK_START_EXPR' or not (self.token_type == 'TK_RESERVED' and token_text in ['var', 'let', 'const'])) and self.flags.last_text != ':': # no need to force newline on VAR - # for (var x = 0... - if token_text == 'if' and self.flags.last_word == 'else' and self.flags.last_text != '{': + if self.token_type == 'TK_RESERVED' and token_text == 'if' and self.flags.last_word == 'else' and self.flags.last_text != '{': self.output_space_before_token = True else: self.flags.var_line = False self.flags.var_line_reindented = False self.append_newline() - elif token_text in self.line_starters and self.flags.last_text != ')': + elif self.token_type == 'TK_RESERVED' and token_text in self.line_starters and self.flags.last_text != ')': self.flags.var_line = False self.flags.var_line_reindented = False self.append_newline() @@ -1173,16 +1177,16 @@ def handle_word(self, token_text): self.append_token(token_text) self.flags.last_word = token_text - if token_text in ['var', 'let', 'const']: + if self.token_type == 'TK_RESERVED' and token_text in ['var', 'let', 'const']: self.flags.var_line = True self.flags.var_line_reindented = False self.flags.var_line_tainted = False - if token_text == 'do': + if self.token_type == 'TK_RESERVED' and token_text == 'do': self.flags.do_block = True - if token_text == 'if': + if self.token_type == 'TK_RESERVED' and token_text == 'if': self.flags.if_block = True @@ -1207,7 +1211,7 @@ def handle_string(self, token_text): # The conditional starts the statement if appropriate. # One difference - strings want at least a space before self.output_space_before_token = True - elif self.last_type == 'TK_WORD': + elif self.last_type == 'TK_RESERVED' or self.last_type == 'TK_WORD': self.output_space_before_token = True elif self.last_type in ['TK_COMMA', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']: if not self.start_of_object_property(): @@ -1267,7 +1271,7 @@ def handle_operator(self, token_text): space_before = True space_after = True - if self.is_special_word(self.flags.last_text): + if self.last_type == 'TK_RESERVED' and self.is_special_word(self.flags.last_text): # return had a special handling in TK_WORD self.output_space_before_token = True self.append_token(token_text) @@ -1311,7 +1315,7 @@ def handle_operator(self, token_text): # ^^ space_before = True - if self.last_type == 'TK_WORD' and self.flags.last_text in self.line_starters: + if self.last_type == 'TK_RESERVED': space_before = True if self.flags.mode == MODE.BlockStatement and self.flags.last_text in ['{', ';']: @@ -1381,7 +1385,7 @@ def handle_comment(self, token_text): def handle_dot(self, token_text): - if self.is_special_word(self.flags.last_text): + if self.last_type == 'TK_RESERVED' and self.is_special_word(self.flags.last_text): self.output_space_before_token = True else: # allow preserved newlines before dots in general diff --git a/python/jsbeautifier/tests/testjsbeautifier.py b/python/jsbeautifier/tests/testjsbeautifier.py index 77622c5df..bb434bb10 100644 --- a/python/jsbeautifier/tests/testjsbeautifier.py +++ b/python/jsbeautifier/tests/testjsbeautifier.py @@ -173,6 +173,12 @@ def test_beautifier(self): # a common snippet in jQuery plugins bt("settings = $.extend({},defaults,settings);", "settings = $.extend({}, defaults, settings);"); + # reserved words used as property names + bt("$http().then().finally().default()", "$http().then().finally().default()"); + bt("$http()\n.then()\n.finally()\n.default()", "$http()\n .then()\n .finally()\n .default()"); + bt("$http().when.in.new.catch().throw()", "$http().when.in.new.catch().throw()"); + bt("$http()\n.when\n.in\n.new\n.catch()\n.throw()", "$http()\n .when\n .in\n .new\n .catch()\n .throw()"); + bt('{xxx;}()', '{\n xxx;\n}()'); bt("a = 'a'\nb = 'b'");