Add operator_position functionality

- Add sanitization of operator_position option.  Throws error if invalid.
 - Possible operator_position options are:
    1) 'before-newline' (default)
    2) 'after-newline'
    3) 'preserve-newline'
 - Move some code around in handle_operator in order to separate
    conflicting logic now that operator newlines are accounted for.
 - Modify allow_wrap_or_preserved_newline to handle operator
    preserved newlines as well.
 - Add -> build tests
 - Within the tests, add an 'inputlib.js' file holding common
    input arrays.  This was necessary due to the matrices not working
    cleanly with default behavior.

Changes not directly relevant to operator_position
 - Add Object.values polyfill
 - Ignore intellijidea project file
 - Fix a few boolean variable assignments within if statements.
    Though not always the case, the expression within the if can
    be assigned directly to the variable
This commit is contained in:
Phil 2015-07-08 00:16:01 -05:00 committed by philip olson
parent a0b5c4b813
commit d83308d87e
10 changed files with 1583 additions and 83 deletions

1
.gitignore vendored
View File

@ -9,3 +9,4 @@ python/dist
python/jsbeautifier.egg-info
target
.idea/

View File

@ -84,6 +84,23 @@
*/
// Object.values polyfill found here:
// http://tokenposts.blogspot.com.au/2012/04/javascript-objectkeys-browser.html
if (!Object.values) {
Object.values = function(o) {
if (o !== Object(o)) {
throw new TypeError('Object.values called on a non-object');
}
var k=[],p;
for (p in o) {
if (Object.prototype.hasOwnProperty.call(o,p)) {
k.push(o[p]);
}
}
return k;
};
}
(function() {
var acorn = {};
@ -179,6 +196,28 @@
return beautifier.beautify();
}
function sanitizeOperatorPosition(opPosition) {
opPosition = opPosition || OPERATOR_POSITION.before_newline;
var validPositionValues = Object.values(OPERATOR_POSITION);
if (!in_array(opPosition, validPositionValues)) {
throw new Error("Invalid Option Value: The option 'operator_position' must be one of the following values\n"
+ validPositionValues
+ "\nYou passed in: '" + opPosition + "'");
}
return opPosition;
}
var OPERATOR_POSITION = {
before_newline: 'before-newline',
after_newline: 'after-newline',
preserve_newline: 'preserve-newline',
};
var OPERATOR_POSITION_BEFORE_OR_PRESERVE = [OPERATOR_POSITION.before_newline, OPERATOR_POSITION.preserve_newline];
var MODE = {
BlockStatement: 'BlockStatement', // 'BLOCK'
Statement: 'Statement', // 'STATEMENT'
@ -288,6 +327,7 @@
opt.e4x = (options.e4x === undefined) ? false : options.e4x;
opt.end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
opt.comma_first = (options.comma_first === undefined) ? false : options.comma_first;
opt.operator_position = sanitizeOperatorPosition(options.operator_position);
// For testing of beautify ignore:start directive
opt.test_output_raw = (options.test_output_raw === undefined) ? false : options.test_output_raw;
@ -440,7 +480,19 @@
return
}
if ((opt.preserve_newlines && current_token.wanted_newline) || force_linewrap) {
var shouldPreserveOrForce = (opt.preserve_newlines && current_token.wanted_newline) || force_linewrap;
var operatorLogicApplies = in_array(flags.last_text, Tokenizer.positionable_operators) || in_array(current_token.text, Tokenizer.positionable_operators);
if (operatorLogicApplies) {
var shouldPrintOperatorNewline = (
in_array(flags.last_text, Tokenizer.positionable_operators) &&
in_array(opt.operator_position, OPERATOR_POSITION_BEFORE_OR_PRESERVE)
)
|| in_array(current_token.text, Tokenizer.positionable_operators);
shouldPreserveOrForce = shouldPreserveOrForce && shouldPrintOperatorNewline;
}
if (shouldPreserveOrForce) {
print_newline(false, true);
} else if (opt.wrap_line_length) {
if (last_type === 'TK_RESERVED' && in_array(flags.last_text, newline_restricted_tokens)) {
@ -1185,6 +1237,18 @@
return;
}
if (current_token.text === '::') {
// no spaces around exotic namespacing syntax operator
print_token();
return;
}
// Allow line wrapping between operators when operator_position is
// set to before or preserve
if (last_type === 'TK_OPERATOR' && in_array(opt.operator_position, OPERATOR_POSITION_BEFORE_OR_PRESERVE)) {
allow_wrap_or_preserved_newline();
}
if (current_token.text === ':' && flags.in_case) {
flags.case_body = true;
indent();
@ -1194,21 +1258,86 @@
return;
}
if (current_token.text === '::') {
// no spaces around exotic namespacing syntax operator
print_token();
return;
}
// Allow line wrapping between operators
if (last_type === 'TK_OPERATOR') {
allow_wrap_or_preserved_newline();
}
var space_before = true;
var space_after = true;
var in_ternary = false;
var isGeneratorAsterisk = current_token.text === '*' && last_type === 'TK_RESERVED' && flags.last_text === 'function';
var isUnary = in_array(current_token.text, ['-', '+']) && (
in_array(last_type, ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']) ||
in_array(flags.last_text, Tokenizer.line_starters) ||
flags.last_text === ','
);
if (in_array(current_token.text, ['--', '++', '!', '~']) || (in_array(current_token.text, ['-', '+']) && (in_array(last_type, ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']) || in_array(flags.last_text, Tokenizer.line_starters) || flags.last_text === ','))) {
if (current_token.text === ':') {
if (flags.ternary_depth === 0) {
// Colon is invalid javascript outside of ternary and object, but do our best to guess what was meant.
space_before = false;
} else {
flags.ternary_depth -= 1;
in_ternary = true;
}
} else if (current_token.text === '?') {
flags.ternary_depth += 1;
}
// let's handle the operator_position option prior to any conflicting logic
if (!isUnary && !isGeneratorAsterisk && opt.preserve_newlines && in_array(current_token.text, Tokenizer.positionable_operators)) {
var isColon = current_token.text === ':';
var isTernaryColon = (isColon && in_ternary);
var isOtherColon = (isColon && !in_ternary);
switch (opt.operator_position) {
case OPERATOR_POSITION.before_newline:
// if the current token is : and it's not a ternary statement then we set space_before to false
output.space_before_token = !isOtherColon;
print_token();
if (!isColon || isTernaryColon) {
allow_wrap_or_preserved_newline();
}
output.space_before_token = true;
return;
case OPERATOR_POSITION.after_newline:
// if the current token is anything but colon, or (via deduction) it's a colon and in a ternary statement,
// then print a newline.
output.space_before_token = true;
if (!isColon || isTernaryColon) {
if (get_token(1).wanted_newline) {
print_newline(false, true);
} else {
allow_wrap_or_preserved_newline();
}
} else {
output.space_before_token = false;
}
print_token();
output.space_before_token = true;
return;
case OPERATOR_POSITION.preserve_newline:
if (!isOtherColon) {
allow_wrap_or_preserved_newline();
}
// if we just added a newline, or the current token is : and it's not a ternary statement,
// then we set space_before to false
space_before = !(output.just_added_newline() || isOtherColon);
output.space_before_token = space_before;
print_token();
output.space_before_token = true;
return;
}
}
if (in_array(current_token.text, ['--', '++', '!', '~']) || isUnary) {
// unary operators (and binary +/- pretending to be unary) special cases
space_before = false;
@ -1250,16 +1379,7 @@
// foo(); --bar;
print_newline();
}
} else if (current_token.text === ':') {
if (flags.ternary_depth === 0) {
// Colon is invalid javascript outside of ternary and object, but do our best to guess what was meant.
space_before = false;
} else {
flags.ternary_depth -= 1;
}
} else if (current_token.text === '?') {
flags.ternary_depth += 1;
} else if (current_token.text === '*' && last_type === 'TK_RESERVED' && flags.last_text === 'function') {
} else if (isGeneratorAsterisk) {
space_before = false;
space_after = false;
}
@ -1273,9 +1393,7 @@
output.add_raw_token(current_token)
if (current_token.directives && current_token.directives['preserve'] === 'end') {
// If we're testing the raw output behavior, do not allow a directive to turn it off.
if (!opt.test_output_raw) {
output.raw = false;
}
output.raw = opt.test_output_raw;
}
return;
}
@ -1308,12 +1426,8 @@
// block comment starts with a new line
print_newline(false, true);
if (lines.length > 1) {
if (all_lines_start_with(lines.slice(1), '*')) {
javadoc = true;
}
else if (each_line_matches_indent(lines.slice(1), lastIndent)) {
starless = true;
}
javadoc = all_lines_start_with(lines.slice(1), '*');
starless = each_line_matches_indent(lines.slice(1), lastIndent);
}
// first line always indented
@ -1613,7 +1727,11 @@
var digit_oct = /[01234567]/;
var digit_hex = /[0123456789abcdefABCDEF]/;
var punct = ('+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! ~ , : ? ^ ^= |= :: => **').split(' ');
this.positionable_operators = '!= !== % & && * ** + - / : < << <= == === > >= >> >>> ? ^ | ||'.split(' ')
var punct = this.positionable_operators.concat(
// non-positionable operators - these do not follow operator position settings
'! %= &= *= ++ += , -- /= :: <<= = => >>= >>>= ^= |= ~'.split(' '))
// words which should always start on new line.
this.line_starters = 'continue,try,throw,return,var,let,const,if,switch,case,default,for,while,break,function,import,export'.split(',');
var reserved_words = this.line_starters.concat(['do', 'in', 'else', 'get', 'set', 'new', 'catch', 'finally', 'typeof', 'yield', 'async', 'await', 'from', 'as']);
@ -2177,7 +2295,6 @@
}
return out;
}
}

View File

@ -66,6 +66,7 @@ var fs = require('fs'),
"e4x": Boolean,
"end_with_newline": Boolean,
"comma_first": Boolean,
"operator_position": ["before-newline", "after-newline", "preserve-newline"],
// CSS-only
"selector_separator_newline": Boolean,
"newline_between_rules": Boolean,
@ -108,6 +109,7 @@ var fs = require('fs'),
"X": ["--e4x"],
"n": ["--end_with_newline"],
"C": ["--comma_first"],
"O": ["--operator_position"],
// CSS-only
"L": ["--selector_separator_newline"],
"N": ["--newline_between_rules"],
@ -259,6 +261,7 @@ function usage(err) {
msg.push(' -X, --e4x Pass E4X xml literals through untouched');
msg.push(' --good-stuff Warm the cockles of Crockford\'s heart');
msg.push(' -C, --comma-first Put commas at the beginning of new line instead of end');
msg.push(' -O, --operator-position Set operator position (before-newline|after-newline|preserve-newline) [before-newline]');
break;
case "html":
msg.push(' -b, --brace-style [collapse|expand|end-expand] ["collapse"]');

View File

@ -28,6 +28,7 @@ function run_javascript_tests(test_obj, Urlencoded, js_beautify, html_beautify,
default_opts.jslint_happy = false;
default_opts.keep_array_indentation = false;
default_opts.brace_style = 'collapse';
default_opts.operator_position = 'before-newline';
function reset_options()
{
@ -498,6 +499,494 @@ function run_javascript_tests(test_obj, Urlencoded, js_beautify, html_beautify,
'}');
reset_options();
//============================================================
// operator_position option - ensure no neswlines if preserve_newlines is false - ()
opts.operator_position = 'before-newline';
opts.preserve_newlines = false;
bt(
'var res = a + b - c / d * e % f;\n' +
'var res = g & h | i ^ j;\n' +
'var res = (k && l || m) ? n : o;\n' +
'var res = p >> q << r >>> s;\n' +
'var res = t === u !== v != w == x >= y <= z > aa < ab;\n' +
'ac + -ad');
bt(
'var res = a + b\n' +
'- c /\n' +
'd * e\n' +
'%\n' +
'f;\n' +
' var res = g & h\n' +
'| i ^\n' +
'j;\n' +
'var res = (k &&\n' +
'l\n' +
'|| m) ?\n' +
'n\n' +
': o\n' +
';\n' +
'var res = p\n' +
'>> q <<\n' +
'r\n' +
'>>> s;\n' +
'var res\n' +
' = t\n' +
'\n' +
' === u !== v\n' +
' !=\n' +
'w\n' +
'== x >=\n' +
'y <= z > aa <\n' +
'ab;\n' +
'ac +\n' +
'-ad',
'var res = a + b - c / d * e % f;\n' +
'var res = g & h | i ^ j;\n' +
'var res = (k && l || m) ? n : o;\n' +
'var res = p >> q << r >>> s;\n' +
'var res = t === u !== v != w == x >= y <= z > aa < ab;\n' +
'ac + -ad');
// operator_position option - ensure no neswlines if preserve_newlines is false - ()
opts.operator_position = 'after-newline';
opts.preserve_newlines = false;
bt(
'var res = a + b - c / d * e % f;\n' +
'var res = g & h | i ^ j;\n' +
'var res = (k && l || m) ? n : o;\n' +
'var res = p >> q << r >>> s;\n' +
'var res = t === u !== v != w == x >= y <= z > aa < ab;\n' +
'ac + -ad');
bt(
'var res = a + b\n' +
'- c /\n' +
'd * e\n' +
'%\n' +
'f;\n' +
' var res = g & h\n' +
'| i ^\n' +
'j;\n' +
'var res = (k &&\n' +
'l\n' +
'|| m) ?\n' +
'n\n' +
': o\n' +
';\n' +
'var res = p\n' +
'>> q <<\n' +
'r\n' +
'>>> s;\n' +
'var res\n' +
' = t\n' +
'\n' +
' === u !== v\n' +
' !=\n' +
'w\n' +
'== x >=\n' +
'y <= z > aa <\n' +
'ab;\n' +
'ac +\n' +
'-ad',
'var res = a + b - c / d * e % f;\n' +
'var res = g & h | i ^ j;\n' +
'var res = (k && l || m) ? n : o;\n' +
'var res = p >> q << r >>> s;\n' +
'var res = t === u !== v != w == x >= y <= z > aa < ab;\n' +
'ac + -ad');
// operator_position option - ensure no neswlines if preserve_newlines is false - ()
opts.operator_position = 'preserve-newline';
opts.preserve_newlines = false;
bt(
'var res = a + b - c / d * e % f;\n' +
'var res = g & h | i ^ j;\n' +
'var res = (k && l || m) ? n : o;\n' +
'var res = p >> q << r >>> s;\n' +
'var res = t === u !== v != w == x >= y <= z > aa < ab;\n' +
'ac + -ad');
bt(
'var res = a + b\n' +
'- c /\n' +
'd * e\n' +
'%\n' +
'f;\n' +
' var res = g & h\n' +
'| i ^\n' +
'j;\n' +
'var res = (k &&\n' +
'l\n' +
'|| m) ?\n' +
'n\n' +
': o\n' +
';\n' +
'var res = p\n' +
'>> q <<\n' +
'r\n' +
'>>> s;\n' +
'var res\n' +
' = t\n' +
'\n' +
' === u !== v\n' +
' !=\n' +
'w\n' +
'== x >=\n' +
'y <= z > aa <\n' +
'ab;\n' +
'ac +\n' +
'-ad',
'var res = a + b - c / d * e % f;\n' +
'var res = g & h | i ^ j;\n' +
'var res = (k && l || m) ? n : o;\n' +
'var res = p >> q << r >>> s;\n' +
'var res = t === u !== v != w == x >= y <= z > aa < ab;\n' +
'ac + -ad');
reset_options();
//============================================================
// operator_position option - set to 'before-newline' (default value)
// comprehensive, various newlines
bt(
'var res = a + b\n' +
'- c /\n' +
'd * e\n' +
'%\n' +
'f;\n' +
' var res = g & h\n' +
'| i ^\n' +
'j;\n' +
'var res = (k &&\n' +
'l\n' +
'|| m) ?\n' +
'n\n' +
': o\n' +
';\n' +
'var res = p\n' +
'>> q <<\n' +
'r\n' +
'>>> s;\n' +
'var res\n' +
' = t\n' +
'\n' +
' === u !== v\n' +
' !=\n' +
'w\n' +
'== x >=\n' +
'y <= z > aa <\n' +
'ab;\n' +
'ac +\n' +
'-ad',
'var res = a + b -\n' +
' c /\n' +
' d * e %\n' +
' f;\n' +
'var res = g & h |\n' +
' i ^\n' +
' j;\n' +
'var res = (k &&\n' +
' l ||\n' +
' m) ?\n' +
' n :\n' +
' o;\n' +
'var res = p >>\n' +
' q <<\n' +
' r >>>\n' +
' s;\n' +
'var res = t\n' +
'\n' +
' ===\n' +
' u !== v !=\n' +
' w ==\n' +
' x >=\n' +
' y <= z > aa <\n' +
' ab;\n' +
'ac +\n' +
' -ad');
// colon special case
bt(
'var a = {\n' +
' b\n' +
': bval,\n' +
' c:\n' +
'cval\n' +
' ,d: dval\n' +
'};\n' +
'var e = f ? g\n' +
': h;\n' +
'var i = j ? k :\n' +
'l;',
'var a = {\n' +
' b: bval,\n' +
' c: cval,\n' +
' d: dval\n' +
'};\n' +
'var e = f ? g :\n' +
' h;\n' +
'var i = j ? k :\n' +
' l;');
// catch-all, includes brackets and other various code
bt(
'var d = 1;\n' +
'if (a === b\n' +
' && c) {\n' +
' d = (c * everything\n' +
' / something_else) %\n' +
' b;\n' +
' e\n' +
' += d;\n' +
'\n' +
'} else if (!(complex && simple) ||\n' +
' (emotion && emotion.name === "happy")) {\n' +
' cryTearsOfJoy(many ||\n' +
' anOcean\n' +
' || aRiver);\n' +
'}',
'var d = 1;\n' +
'if (a === b &&\n' +
' c) {\n' +
' d = (c * everything /\n' +
' something_else) %\n' +
' b;\n' +
' e\n' +
' += d;\n' +
'\n' +
'} else if (!(complex && simple) ||\n' +
' (emotion && emotion.name === "happy")) {\n' +
' cryTearsOfJoy(many ||\n' +
' anOcean ||\n' +
' aRiver);\n' +
'}');
reset_options();
//============================================================
// operator_position option - set to 'after_newline'
opts.operator_position = 'after-newline';
// comprehensive, various newlines
bt(
'var res = a + b\n' +
'- c /\n' +
'd * e\n' +
'%\n' +
'f;\n' +
' var res = g & h\n' +
'| i ^\n' +
'j;\n' +
'var res = (k &&\n' +
'l\n' +
'|| m) ?\n' +
'n\n' +
': o\n' +
';\n' +
'var res = p\n' +
'>> q <<\n' +
'r\n' +
'>>> s;\n' +
'var res\n' +
' = t\n' +
'\n' +
' === u !== v\n' +
' !=\n' +
'w\n' +
'== x >=\n' +
'y <= z > aa <\n' +
'ab;\n' +
'ac +\n' +
'-ad',
'var res = a + b\n' +
' - c\n' +
' / d * e\n' +
' % f;\n' +
'var res = g & h\n' +
' | i\n' +
' ^ j;\n' +
'var res = (k\n' +
' && l\n' +
' || m)\n' +
' ? n\n' +
' : o;\n' +
'var res = p\n' +
' >> q\n' +
' << r\n' +
' >>> s;\n' +
'var res = t\n' +
'\n' +
' === u !== v\n' +
' != w\n' +
' == x\n' +
' >= y <= z > aa\n' +
' < ab;\n' +
'ac\n' +
' + -ad');
// colon special case
bt(
'var a = {\n' +
' b\n' +
': bval,\n' +
' c:\n' +
'cval\n' +
' ,d: dval\n' +
'};\n' +
'var e = f ? g\n' +
': h;\n' +
'var i = j ? k :\n' +
'l;',
'var a = {\n' +
' b: bval,\n' +
' c: cval,\n' +
' d: dval\n' +
'};\n' +
'var e = f ? g\n' +
' : h;\n' +
'var i = j ? k\n' +
' : l;');
// catch-all, includes brackets and other various code
bt(
'var d = 1;\n' +
'if (a === b\n' +
' && c) {\n' +
' d = (c * everything\n' +
' / something_else) %\n' +
' b;\n' +
' e\n' +
' += d;\n' +
'\n' +
'} else if (!(complex && simple) ||\n' +
' (emotion && emotion.name === "happy")) {\n' +
' cryTearsOfJoy(many ||\n' +
' anOcean\n' +
' || aRiver);\n' +
'}',
'var d = 1;\n' +
'if (a === b\n' +
' && c) {\n' +
' d = (c * everything\n' +
' / something_else)\n' +
' % b;\n' +
' e\n' +
' += d;\n' +
'\n' +
'} else if (!(complex && simple)\n' +
' || (emotion && emotion.name === "happy")) {\n' +
' cryTearsOfJoy(many\n' +
' || anOcean\n' +
' || aRiver);\n' +
'}');
reset_options();
//============================================================
// operator_position option - set to 'preserve-newline'
opts.operator_position = 'preserve-newline';
// comprehensive, various newlines
bt(
'var res = a + b\n' +
'- c /\n' +
'd * e\n' +
'%\n' +
'f;\n' +
' var res = g & h\n' +
'| i ^\n' +
'j;\n' +
'var res = (k &&\n' +
'l\n' +
'|| m) ?\n' +
'n\n' +
': o\n' +
';\n' +
'var res = p\n' +
'>> q <<\n' +
'r\n' +
'>>> s;\n' +
'var res\n' +
' = t\n' +
'\n' +
' === u !== v\n' +
' !=\n' +
'w\n' +
'== x >=\n' +
'y <= z > aa <\n' +
'ab;\n' +
'ac +\n' +
'-ad',
'var res = a + b\n' +
' - c /\n' +
' d * e\n' +
' %\n' +
' f;\n' +
'var res = g & h\n' +
' | i ^\n' +
' j;\n' +
'var res = (k &&\n' +
' l\n' +
' || m) ?\n' +
' n\n' +
' : o;\n' +
'var res = p\n' +
' >> q <<\n' +
' r\n' +
' >>> s;\n' +
'var res = t\n' +
'\n' +
' === u !== v\n' +
' !=\n' +
' w\n' +
' == x >=\n' +
' y <= z > aa <\n' +
' ab;\n' +
'ac +\n' +
' -ad');
// colon special case
bt(
'var a = {\n' +
' b\n' +
': bval,\n' +
' c:\n' +
'cval\n' +
' ,d: dval\n' +
'};\n' +
'var e = f ? g\n' +
': h;\n' +
'var i = j ? k :\n' +
'l;',
'var a = {\n' +
' b: bval,\n' +
' c: cval,\n' +
' d: dval\n' +
'};\n' +
'var e = f ? g\n' +
' : h;\n' +
'var i = j ? k :\n' +
' l;');
// catch-all, includes brackets and other various code
bt(
'var d = 1;\n' +
'if (a === b\n' +
' && c) {\n' +
' d = (c * everything\n' +
' / something_else) %\n' +
' b;\n' +
' e\n' +
' += d;\n' +
'\n' +
'} else if (!(complex && simple) ||\n' +
' (emotion && emotion.name === "happy")) {\n' +
' cryTearsOfJoy(many ||\n' +
' anOcean\n' +
' || aRiver);\n' +
'}');
reset_options();
//============================================================
// New Test Suite
@ -2437,7 +2926,7 @@ function run_javascript_tests(test_obj, Urlencoded, js_beautify, html_beautify,
// Line wrap test intputs
//.............---------1---------2---------3---------4---------5---------6---------7
//.............1234567890123456789012345678901234567890123456789012345678901234567890
wrap_input_1=('foo.bar().baz().cucumber((fat && "sassy") || (leans\n&& mean));\n' +
wrap_input_1=('foo.bar().baz().cucumber((fat && "sassy") || (leans && mean));\n' +
'Test_very_long_variable_name_this_should_never_wrap\n.but_this_can\n' +
'return between_return_and_expression_should_never_wrap.but_this_can\n' +
'throw between_throw_and_expression_should_never_wrap.but_this_can\n' +
@ -2452,7 +2941,7 @@ function run_javascript_tests(test_obj, Urlencoded, js_beautify, html_beautify,
//.............---------1---------2---------3---------4---------5---------6---------7
//.............1234567890123456789012345678901234567890123456789012345678901234567890
wrap_input_2=('{\n' +
' foo.bar().baz().cucumber((fat && "sassy") || (leans\n&& mean));\n' +
' foo.bar().baz().cucumber((fat && "sassy") || (leans && mean));\n' +
' Test_very_long_variable_name_this_should_never_wrap\n.but_this_can\n' +
' return between_return_and_expression_should_never_wrap.but_this_can\n' +
' throw between_throw_and_expression_should_never_wrap.but_this_can\n' +
@ -2867,8 +3356,8 @@ function run_javascript_tests(test_obj, Urlencoded, js_beautify, html_beautify,
bt('var a = /*i*/\n"b";', 'var a = /*i*/\n "b";');
bt('var a = /*i*/\nb;', 'var a = /*i*/\n b;');
bt('{\n\n\n"x"\n}', '{\n\n\n "x"\n}');
bt('if(a &&\nb\n||\nc\n||d\n&&\ne) e = f', 'if (a &&\n b ||\n c || d &&\n e) e = f');
bt('if(a &&\n(b\n||\nc\n||d)\n&&\ne) e = f', 'if (a &&\n (b ||\n c || d) &&\n e) e = f');
bt('if(a &&\nb\n||\nc\n||d\n&&\ne) e = f', 'if (a &&\n b ||\n c ||\n d &&\n e) e = f');
bt('if(a &&\n(b\n||\nc\n||d)\n&&\ne) e = f', 'if (a &&\n (b ||\n c ||\n d) &&\n e) e = f');
test_fragment('\n\n"x"', '"x"');
// this beavior differs between js and python, defaults to unlimited in js, 10 in python

View File

@ -82,6 +82,7 @@ class BeautifierOptions:
self.break_chained_methods = False
self.end_with_newline = False
self.comma_first = False
self.operator_position = 'before-newline'
# For testing of beautify ignore:start directive
self.test_output_raw = False
@ -319,7 +320,22 @@ Rarely needed options:
else:
return 0
OPERATOR_POSITION = {
'before_newline': 'before-newline',
'after_newline': 'after-newline',
'preserve_newline': 'preserve-newline'
}
OPERATOR_POSITION_BEFORE_OR_PRESERVE = [OPERATOR_POSITION['before_newline'], OPERATOR_POSITION['preserve_newline']];
def sanitizeOperatorPosition(opPosition):
if not opPosition:
return OPERATOR_POSITION['before_newline']
elif opPosition not in OPERATOR_POSITION.values():
raise ValueError("Invalid Option Value: The option 'operator_position' must be one of the following values\n" +
str(OPERATOR_POSITION.values()) +
"\nYou passed in: '" + opPosition + "'")
return opPosition
class MODE:
BlockStatement, Statement, ObjectLiteral, ArrayLiteral, \
@ -484,7 +500,15 @@ class Beautifier:
if self.output.just_added_newline():
return
if (self.opts.preserve_newlines and current_token.wanted_newline) or force_linewrap:
shouldPreserveOrForce = (self.opts.preserve_newlines and current_token.wanted_newline) or force_linewrap
operatorLogicApplies = self.flags.last_text in Tokenizer.positionable_operators or current_token.text in Tokenizer.positionable_operators
if operatorLogicApplies:
shouldPrintOperatorNewline = (self.flags.last_text in Tokenizer.positionable_operators and self.opts.operator_position in OPERATOR_POSITION_BEFORE_OR_PRESERVE) \
or current_token.text in Tokenizer.positionable_operators
shouldPreserveOrForce = shouldPreserveOrForce and shouldPrintOperatorNewline
if shouldPreserveOrForce:
self.print_newline(preserve_statement_flags = True)
elif self.opts.wrap_line_length > 0:
if self.last_type == 'TK_RESERVED' and self.flags.last_text in self._newline_restricted_tokens:
@ -1106,6 +1130,15 @@ class Beautifier:
self.print_token(current_token)
return
if current_token.text == '::':
# no spaces around the exotic namespacing syntax operator
self.print_token(current_token)
return
# Allow line wrapping between operators when operator_position is
# set to before or preserve
if self.last_type == 'TK_OPERATOR' and self.opts.operator_position in OPERATOR_POSITION_BEFORE_OR_PRESERVE:
self.allow_wrap_or_preserved_newline(current_token)
if current_token.text == ':' and self.flags.in_case:
self.flags.case_body = True
@ -1115,23 +1148,78 @@ class Beautifier:
self.flags.in_case = False
return
if current_token.text == '::':
# no spaces around the exotic namespacing syntax operator
self.print_token(current_token)
return
# Allow line wrapping between operators in an expression
if self.last_type == 'TK_OPERATOR':
self.allow_wrap_or_preserved_newline(current_token)
space_before = True
space_after = True
in_ternary = False
isGeneratorAsterisk = current_token.text == '*' and self.last_type == 'TK_RESERVED' and self.flags.last_text == 'function'
isUnary = current_token.text in ['+', '-'] \
and (self.last_type in ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR'] \
or self.flags.last_text in Tokenizer.line_starters or self.flags.last_text == ',')
if current_token.text in ['--', '++', '!', '~'] \
or (current_token.text in ['+', '-'] \
and (self.last_type in ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR'] \
or self.flags.last_text in Tokenizer.line_starters or self.flags.last_text == ',')):
if current_token.text == ':':
if self.flags.ternary_depth == 0:
# Colon is invalid javascript outside of ternary and object, but do our best to guess what was meant.
space_before = False
else:
self.flags.ternary_depth -= 1
in_ternary = True
elif current_token.text == '?':
self.flags.ternary_depth += 1
# let's handle the operator_position option prior to any conflicting logic
if (not isUnary) and (not isGeneratorAsterisk) and \
self.opts.preserve_newlines and current_token.text in Tokenizer.positionable_operators:
isColon = current_token.text == ':'
isTernaryColon = isColon and in_ternary
isOtherColon = isColon and not in_ternary
if self.opts.operator_position == OPERATOR_POSITION['before_newline']:
# if the current token is : and it's not a ternary statement then we set space_before to false
self.output.space_before_token = not isOtherColon
self.print_token(current_token)
if (not isColon) or isTernaryColon:
self.allow_wrap_or_preserved_newline(current_token)
self.output.space_before_token = True
return
elif self.opts.operator_position == OPERATOR_POSITION['after_newline']:
# if the current token is anything but colon, or (via deduction) it's a colon and in a ternary statement,
# then print a newline.
self.output.space_before_token = True
if (not isColon) or isTernaryColon:
if self.get_token(1).wanted_newline:
self.print_newline(preserve_statement_flags = True)
else:
self.allow_wrap_or_preserved_newline(current_token)
else:
self.output.space_before_token = False
self.print_token(current_token)
self.output.space_before_token = True
return
elif self.opts.operator_position == OPERATOR_POSITION['preserve_newline']:
if not isOtherColon:
self.allow_wrap_or_preserved_newline(current_token)
# if we just added a newline, or the current token is : and it's not a ternary statement,
# then we set space_before to false
self.output.space_before_token = not (self.output.just_added_newline() or isOtherColon)
self.print_token(current_token)
self.output.space_before_token = True
return
if current_token.text in ['--', '++', '!', '~'] or isUnary:
space_before = False
space_after = False
@ -1166,15 +1254,7 @@ class Beautifier:
# foo(): --bar
self.print_newline()
elif current_token.text == ':':
if self.flags.ternary_depth == 0:
# Colon is invalid javascript outside of ternary and object, but do our best to guess what was meant.
space_before = False
else:
self.flags.ternary_depth -= 1
elif current_token.text == '?':
self.flags.ternary_depth += 1
elif current_token.text == '*' and self.last_type == 'TK_RESERVED' and self.flags.last_text == 'function':
elif isGeneratorAsterisk:
space_before = False
space_after = False
@ -1193,8 +1273,7 @@ class Beautifier:
self.output.add_raw_token(current_token)
if current_token.directives and current_token.directives.get('preserve') == 'end':
# If we're testing the raw output behavior, do not allow a directive to turn it off.
if not self.opts.test_output_raw:
self.output.raw = False
self.output.raw = self.opts.test_output_raw
return
if current_token.directives:
@ -1222,10 +1301,8 @@ class Beautifier:
# block comment starts with a new line
self.print_newline(preserve_statement_flags = True)
if len(lines) > 1:
if not any(l for l in lines[1:] if ( l.strip() == '' or (l.lstrip())[0] != '*')):
javadoc = True
elif all(l.startswith(last_indent) or l.strip() == '' for l in lines[1:]):
starless = True
javadoc = not any(l for l in lines[1:] if ( l.strip() == '' or (l.lstrip())[0] != '*'))
starless = all(l.startswith(last_indent) or l.strip() == '' for l in lines[1:])
# first line always indented
self.print_token(current_token, lines[0])
@ -1465,7 +1542,11 @@ class Tokenizer:
digit_bin = re.compile('[01]')
digit_oct = re.compile('[01234567]')
digit_hex = re.compile('[0123456789abcdefABCDEF]')
punct = ('+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! ~ , : ? ^ ^= |= :: => **').split(' ')
positionable_operators = '!= !== % & && * ** + - / : < << <= == === > >= >> >>> ? ^ | ||'.split(' ')
punct = (positionable_operators +
# non-positionable operators - these do not follow operator position settings
'! %= &= *= ++ += , -- /= :: <<= = => >>= >>>= ^= |= ~'.split(' '))
# Words which always should start on a new line
line_starters = 'continue,try,throw,return,var,let,const,if,switch,case,default,for,while,break,function,import,export'.split(',')
@ -1905,12 +1986,12 @@ def main():
argv = sys.argv[1:]
try:
opts, args = getopt.getopt(argv, "s:c:e:o:rdEPjabkil:xhtfvXnCw:",
opts, args = getopt.getopt(argv, "s:c:e:o:rdEPjabkil:xhtfvXnCO:w:",
['indent-size=','indent-char=','eol=''outfile=', 'replace', 'disable-preserve-newlines',
'space-in-paren', 'space-in-empty-paren', 'jslint-happy', 'space-after-anon-function',
'brace-style=', 'keep-array-indentation', 'indent-level=', 'unescape-strings', 'help',
'usage', 'stdin', 'eval-code', 'indent-with-tabs', 'keep-function-indentation', 'version',
'e4x', 'end-with-newline','comma-first','wrap-line-length'])
'e4x', 'end-with-newline','comma-first','operator-position=','wrap-line-length'])
except getopt.GetoptError as ex:
print(ex, file=sys.stderr)
return usage(sys.stderr)
@ -1962,6 +2043,8 @@ def main():
js_options.end_with_newline = True
elif opt in ('--comma-first', '-C'):
js_options.comma_first = True
elif opt in ('--operator-position', '-O'):
js_options.operator_position = sanitizeOperatorPosition(arg)
elif opt in ('--wrap-line-length ', '-w'):
js_options.wrap_line_length = int(arg)
elif opt in ('--stdin', '-i'):

View File

@ -39,6 +39,7 @@ class TestJSBeautifier(unittest.TestCase):
default_options.jslint_happy = false
default_options.keep_array_indentation = false
default_options.brace_style = 'collapse'
default_options.operator_position = 'before-newline'
cls.default_options = default_options
cls.wrapregex = re.compile('^(.+)$', re.MULTILINE)
@ -324,6 +325,494 @@ class TestJSBeautifier(unittest.TestCase):
'}')
self.reset_options();
#============================================================
# operator_position option - ensure no neswlines if preserve_newlines is false - ()
self.options.operator_position = 'before-newline'
self.options.preserve_newlines = false
bt(
'var res = a + b - c / d * e % f;\n' +
'var res = g & h | i ^ j;\n' +
'var res = (k && l || m) ? n : o;\n' +
'var res = p >> q << r >>> s;\n' +
'var res = t === u !== v != w == x >= y <= z > aa < ab;\n' +
'ac + -ad')
bt(
'var res = a + b\n' +
'- c /\n' +
'd * e\n' +
'%\n' +
'f;\n' +
' var res = g & h\n' +
'| i ^\n' +
'j;\n' +
'var res = (k &&\n' +
'l\n' +
'|| m) ?\n' +
'n\n' +
': o\n' +
';\n' +
'var res = p\n' +
'>> q <<\n' +
'r\n' +
'>>> s;\n' +
'var res\n' +
' = t\n' +
'\n' +
' === u !== v\n' +
' !=\n' +
'w\n' +
'== x >=\n' +
'y <= z > aa <\n' +
'ab;\n' +
'ac +\n' +
'-ad',
'var res = a + b - c / d * e % f;\n' +
'var res = g & h | i ^ j;\n' +
'var res = (k && l || m) ? n : o;\n' +
'var res = p >> q << r >>> s;\n' +
'var res = t === u !== v != w == x >= y <= z > aa < ab;\n' +
'ac + -ad')
# operator_position option - ensure no neswlines if preserve_newlines is false - ()
self.options.operator_position = 'after-newline'
self.options.preserve_newlines = false
bt(
'var res = a + b - c / d * e % f;\n' +
'var res = g & h | i ^ j;\n' +
'var res = (k && l || m) ? n : o;\n' +
'var res = p >> q << r >>> s;\n' +
'var res = t === u !== v != w == x >= y <= z > aa < ab;\n' +
'ac + -ad')
bt(
'var res = a + b\n' +
'- c /\n' +
'd * e\n' +
'%\n' +
'f;\n' +
' var res = g & h\n' +
'| i ^\n' +
'j;\n' +
'var res = (k &&\n' +
'l\n' +
'|| m) ?\n' +
'n\n' +
': o\n' +
';\n' +
'var res = p\n' +
'>> q <<\n' +
'r\n' +
'>>> s;\n' +
'var res\n' +
' = t\n' +
'\n' +
' === u !== v\n' +
' !=\n' +
'w\n' +
'== x >=\n' +
'y <= z > aa <\n' +
'ab;\n' +
'ac +\n' +
'-ad',
'var res = a + b - c / d * e % f;\n' +
'var res = g & h | i ^ j;\n' +
'var res = (k && l || m) ? n : o;\n' +
'var res = p >> q << r >>> s;\n' +
'var res = t === u !== v != w == x >= y <= z > aa < ab;\n' +
'ac + -ad')
# operator_position option - ensure no neswlines if preserve_newlines is false - ()
self.options.operator_position = 'preserve-newline'
self.options.preserve_newlines = false
bt(
'var res = a + b - c / d * e % f;\n' +
'var res = g & h | i ^ j;\n' +
'var res = (k && l || m) ? n : o;\n' +
'var res = p >> q << r >>> s;\n' +
'var res = t === u !== v != w == x >= y <= z > aa < ab;\n' +
'ac + -ad')
bt(
'var res = a + b\n' +
'- c /\n' +
'd * e\n' +
'%\n' +
'f;\n' +
' var res = g & h\n' +
'| i ^\n' +
'j;\n' +
'var res = (k &&\n' +
'l\n' +
'|| m) ?\n' +
'n\n' +
': o\n' +
';\n' +
'var res = p\n' +
'>> q <<\n' +
'r\n' +
'>>> s;\n' +
'var res\n' +
' = t\n' +
'\n' +
' === u !== v\n' +
' !=\n' +
'w\n' +
'== x >=\n' +
'y <= z > aa <\n' +
'ab;\n' +
'ac +\n' +
'-ad',
'var res = a + b - c / d * e % f;\n' +
'var res = g & h | i ^ j;\n' +
'var res = (k && l || m) ? n : o;\n' +
'var res = p >> q << r >>> s;\n' +
'var res = t === u !== v != w == x >= y <= z > aa < ab;\n' +
'ac + -ad')
self.reset_options();
#============================================================
# operator_position option - set to 'before-newline' (default value)
# comprehensive, various newlines
bt(
'var res = a + b\n' +
'- c /\n' +
'd * e\n' +
'%\n' +
'f;\n' +
' var res = g & h\n' +
'| i ^\n' +
'j;\n' +
'var res = (k &&\n' +
'l\n' +
'|| m) ?\n' +
'n\n' +
': o\n' +
';\n' +
'var res = p\n' +
'>> q <<\n' +
'r\n' +
'>>> s;\n' +
'var res\n' +
' = t\n' +
'\n' +
' === u !== v\n' +
' !=\n' +
'w\n' +
'== x >=\n' +
'y <= z > aa <\n' +
'ab;\n' +
'ac +\n' +
'-ad',
'var res = a + b -\n' +
' c /\n' +
' d * e %\n' +
' f;\n' +
'var res = g & h |\n' +
' i ^\n' +
' j;\n' +
'var res = (k &&\n' +
' l ||\n' +
' m) ?\n' +
' n :\n' +
' o;\n' +
'var res = p >>\n' +
' q <<\n' +
' r >>>\n' +
' s;\n' +
'var res = t\n' +
'\n' +
' ===\n' +
' u !== v !=\n' +
' w ==\n' +
' x >=\n' +
' y <= z > aa <\n' +
' ab;\n' +
'ac +\n' +
' -ad')
# colon special case
bt(
'var a = {\n' +
' b\n' +
': bval,\n' +
' c:\n' +
'cval\n' +
' ,d: dval\n' +
'};\n' +
'var e = f ? g\n' +
': h;\n' +
'var i = j ? k :\n' +
'l;',
'var a = {\n' +
' b: bval,\n' +
' c: cval,\n' +
' d: dval\n' +
'};\n' +
'var e = f ? g :\n' +
' h;\n' +
'var i = j ? k :\n' +
' l;')
# catch-all, includes brackets and other various code
bt(
'var d = 1;\n' +
'if (a === b\n' +
' && c) {\n' +
' d = (c * everything\n' +
' / something_else) %\n' +
' b;\n' +
' e\n' +
' += d;\n' +
'\n' +
'} else if (!(complex && simple) ||\n' +
' (emotion && emotion.name === "happy")) {\n' +
' cryTearsOfJoy(many ||\n' +
' anOcean\n' +
' || aRiver);\n' +
'}',
'var d = 1;\n' +
'if (a === b &&\n' +
' c) {\n' +
' d = (c * everything /\n' +
' something_else) %\n' +
' b;\n' +
' e\n' +
' += d;\n' +
'\n' +
'} else if (!(complex && simple) ||\n' +
' (emotion && emotion.name === "happy")) {\n' +
' cryTearsOfJoy(many ||\n' +
' anOcean ||\n' +
' aRiver);\n' +
'}')
self.reset_options();
#============================================================
# operator_position option - set to 'after_newline'
self.options.operator_position = 'after-newline'
# comprehensive, various newlines
bt(
'var res = a + b\n' +
'- c /\n' +
'd * e\n' +
'%\n' +
'f;\n' +
' var res = g & h\n' +
'| i ^\n' +
'j;\n' +
'var res = (k &&\n' +
'l\n' +
'|| m) ?\n' +
'n\n' +
': o\n' +
';\n' +
'var res = p\n' +
'>> q <<\n' +
'r\n' +
'>>> s;\n' +
'var res\n' +
' = t\n' +
'\n' +
' === u !== v\n' +
' !=\n' +
'w\n' +
'== x >=\n' +
'y <= z > aa <\n' +
'ab;\n' +
'ac +\n' +
'-ad',
'var res = a + b\n' +
' - c\n' +
' / d * e\n' +
' % f;\n' +
'var res = g & h\n' +
' | i\n' +
' ^ j;\n' +
'var res = (k\n' +
' && l\n' +
' || m)\n' +
' ? n\n' +
' : o;\n' +
'var res = p\n' +
' >> q\n' +
' << r\n' +
' >>> s;\n' +
'var res = t\n' +
'\n' +
' === u !== v\n' +
' != w\n' +
' == x\n' +
' >= y <= z > aa\n' +
' < ab;\n' +
'ac\n' +
' + -ad')
# colon special case
bt(
'var a = {\n' +
' b\n' +
': bval,\n' +
' c:\n' +
'cval\n' +
' ,d: dval\n' +
'};\n' +
'var e = f ? g\n' +
': h;\n' +
'var i = j ? k :\n' +
'l;',
'var a = {\n' +
' b: bval,\n' +
' c: cval,\n' +
' d: dval\n' +
'};\n' +
'var e = f ? g\n' +
' : h;\n' +
'var i = j ? k\n' +
' : l;')
# catch-all, includes brackets and other various code
bt(
'var d = 1;\n' +
'if (a === b\n' +
' && c) {\n' +
' d = (c * everything\n' +
' / something_else) %\n' +
' b;\n' +
' e\n' +
' += d;\n' +
'\n' +
'} else if (!(complex && simple) ||\n' +
' (emotion && emotion.name === "happy")) {\n' +
' cryTearsOfJoy(many ||\n' +
' anOcean\n' +
' || aRiver);\n' +
'}',
'var d = 1;\n' +
'if (a === b\n' +
' && c) {\n' +
' d = (c * everything\n' +
' / something_else)\n' +
' % b;\n' +
' e\n' +
' += d;\n' +
'\n' +
'} else if (!(complex && simple)\n' +
' || (emotion && emotion.name === "happy")) {\n' +
' cryTearsOfJoy(many\n' +
' || anOcean\n' +
' || aRiver);\n' +
'}')
self.reset_options();
#============================================================
# operator_position option - set to 'preserve-newline'
self.options.operator_position = 'preserve-newline'
# comprehensive, various newlines
bt(
'var res = a + b\n' +
'- c /\n' +
'd * e\n' +
'%\n' +
'f;\n' +
' var res = g & h\n' +
'| i ^\n' +
'j;\n' +
'var res = (k &&\n' +
'l\n' +
'|| m) ?\n' +
'n\n' +
': o\n' +
';\n' +
'var res = p\n' +
'>> q <<\n' +
'r\n' +
'>>> s;\n' +
'var res\n' +
' = t\n' +
'\n' +
' === u !== v\n' +
' !=\n' +
'w\n' +
'== x >=\n' +
'y <= z > aa <\n' +
'ab;\n' +
'ac +\n' +
'-ad',
'var res = a + b\n' +
' - c /\n' +
' d * e\n' +
' %\n' +
' f;\n' +
'var res = g & h\n' +
' | i ^\n' +
' j;\n' +
'var res = (k &&\n' +
' l\n' +
' || m) ?\n' +
' n\n' +
' : o;\n' +
'var res = p\n' +
' >> q <<\n' +
' r\n' +
' >>> s;\n' +
'var res = t\n' +
'\n' +
' === u !== v\n' +
' !=\n' +
' w\n' +
' == x >=\n' +
' y <= z > aa <\n' +
' ab;\n' +
'ac +\n' +
' -ad')
# colon special case
bt(
'var a = {\n' +
' b\n' +
': bval,\n' +
' c:\n' +
'cval\n' +
' ,d: dval\n' +
'};\n' +
'var e = f ? g\n' +
': h;\n' +
'var i = j ? k :\n' +
'l;',
'var a = {\n' +
' b: bval,\n' +
' c: cval,\n' +
' d: dval\n' +
'};\n' +
'var e = f ? g\n' +
' : h;\n' +
'var i = j ? k :\n' +
' l;')
# catch-all, includes brackets and other various code
bt(
'var d = 1;\n' +
'if (a === b\n' +
' && c) {\n' +
' d = (c * everything\n' +
' / something_else) %\n' +
' b;\n' +
' e\n' +
' += d;\n' +
'\n' +
'} else if (!(complex && simple) ||\n' +
' (emotion && emotion.name === "happy")) {\n' +
' cryTearsOfJoy(many ||\n' +
' anOcean\n' +
' || aRiver);\n' +
'}')
self.reset_options();
#============================================================
# New Test Suite
@ -2598,7 +3087,7 @@ class TestJSBeautifier(unittest.TestCase):
# Line wrap test intputs
#..............---------1---------2---------3---------4---------5---------6---------7
#..............1234567890123456789012345678901234567890123456789012345678901234567890
wrap_input_1=('foo.bar().baz().cucumber((fat && "sassy") || (leans\n&& mean));\n' +
wrap_input_1=('foo.bar().baz().cucumber((fat && "sassy") || (leans && mean));\n' +
'Test_very_long_variable_name_this_should_never_wrap\n.but_this_can\n' +
'return between_return_and_expression_should_never_wrap.but_this_can\n' +
'throw between_throw_and_expression_should_never_wrap.but_this_can\n' +
@ -2613,7 +3102,7 @@ class TestJSBeautifier(unittest.TestCase):
#..............---------1---------2---------3---------4---------5---------6---------7
#..............1234567890123456789012345678901234567890123456789012345678901234567890
wrap_input_2=('{\n' +
' foo.bar().baz().cucumber((fat && "sassy") || (leans\n&& mean));\n' +
' foo.bar().baz().cucumber((fat && "sassy") || (leans && mean));\n' +
' Test_very_long_variable_name_this_should_never_wrap\n.but_this_can\n' +
' return between_return_and_expression_should_never_wrap.but_this_can\n' +
' throw between_throw_and_expression_should_never_wrap.but_this_can\n' +
@ -3033,8 +3522,8 @@ class TestJSBeautifier(unittest.TestCase):
bt('var a = /*i*/\n"b";', 'var a = /*i*/\n "b";')
bt('var a = /*i*/\nb;', 'var a = /*i*/\n b;')
bt('{\n\n\n"x"\n}', '{\n\n\n "x"\n}')
bt('if(a &&\nb\n||\nc\n||d\n&&\ne) e = f', 'if (a &&\n b ||\n c || d &&\n e) e = f')
bt('if(a &&\n(b\n||\nc\n||d)\n&&\ne) e = f', 'if (a &&\n (b ||\n c || d) &&\n e) e = f')
bt('if(a &&\nb\n||\nc\n||d\n&&\ne) e = f', 'if (a &&\n b ||\n c ||\n d &&\n e) e = f')
bt('if(a &&\n(b\n||\nc\n||d)\n&&\ne) e = f', 'if (a &&\n (b ||\n c ||\n d) &&\n e) e = f')
test_fragment('\n\n"x"', '"x"')
# this beavior differs between js and python, defaults to unlimited in js, 10 in python
bt('a = 1;\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nb = 2;',

View File

@ -0,0 +1,88 @@
'use strict';
//--------//
// Inputs //
//--------//
var ops = ['>', '<', '+', '-', '*', '/', '%', '&', '|', '^', '?', ':'];
var operator_position = {
sanity: [
'var res = a + b - c / d * e % f;',
'var res = g & h | i ^ j;',
'var res = (k && l || m) ? n : o;',
'var res = p >> q << r >>> s;',
'var res = t === u !== v != w == x >= y <= z > aa < ab;',
'ac + -ad'
],
comprehensive: [
'var res = a + b',
'- c /',
'd * e',
'%',
'f;',
' var res = g & h',
'| i ^',
'j;',
'var res = (k &&',
'l',
'|| m) ?',
'n',
': o',
';',
'var res = p',
'>> q <<',
'r',
'>>> s;',
'var res',
' = t',
'',
' === u !== v',
' !=',
'w',
'== x >=',
'y <= z > aa <',
'ab;',
'ac +',
'-ad'
],
colon_special_case: [
'var a = {',
' b',
': bval,',
' c:',
'cval',
' ,d: dval',
'};',
'var e = f ? g',
': h;',
'var i = j ? k :',
'l;'
],
catch_all: [
'var d = 1;',
'if (a === b',
' && c) {',
' d = (c * everything',
' / something_else) %',
' b;',
' e',
' += d;',
'',
'} else if (!(complex && simple) ||',
' (emotion && emotion.name === "happy")) {',
' cryTearsOfJoy(many ||',
' anOcean',
' || aRiver);',
'}'
]
};
//---------//
// Exports //
//---------//
module.exports = {
operator_position: operator_position
}

View File

@ -611,7 +611,7 @@ function run_javascript_tests(test_obj, Urlencoded, js_beautify, html_beautify,
// Line wrap test intputs
//.............---------1---------2---------3---------4---------5---------6---------7
//.............1234567890123456789012345678901234567890123456789012345678901234567890
wrap_input_1=('foo.bar().baz().cucumber((fat && "sassy") || (leans\n&& mean));\n' +
wrap_input_1=('foo.bar().baz().cucumber((fat && "sassy") || (leans && mean));\n' +
'Test_very_long_variable_name_this_should_never_wrap\n.but_this_can\n' +
'return between_return_and_expression_should_never_wrap.but_this_can\n' +
'throw between_throw_and_expression_should_never_wrap.but_this_can\n' +
@ -626,7 +626,7 @@ function run_javascript_tests(test_obj, Urlencoded, js_beautify, html_beautify,
//.............---------1---------2---------3---------4---------5---------6---------7
//.............1234567890123456789012345678901234567890123456789012345678901234567890
wrap_input_2=('{\n' +
' foo.bar().baz().cucumber((fat && "sassy") || (leans\n&& mean));\n' +
' foo.bar().baz().cucumber((fat && "sassy") || (leans && mean));\n' +
' Test_very_long_variable_name_this_should_never_wrap\n.but_this_can\n' +
' return between_return_and_expression_should_never_wrap.but_this_can\n' +
' throw between_throw_and_expression_should_never_wrap.but_this_can\n' +
@ -1041,8 +1041,8 @@ function run_javascript_tests(test_obj, Urlencoded, js_beautify, html_beautify,
bt('var a = /*i*/\n"b";', 'var a = /*i*/\n "b";');
bt('var a = /*i*/\nb;', 'var a = /*i*/\n b;');
bt('{\n\n\n"x"\n}', '{\n\n\n "x"\n}');
bt('if(a &&\nb\n||\nc\n||d\n&&\ne) e = f', 'if (a &&\n b ||\n c || d &&\n e) e = f');
bt('if(a &&\n(b\n||\nc\n||d)\n&&\ne) e = f', 'if (a &&\n (b ||\n c || d) &&\n e) e = f');
bt('if(a &&\nb\n||\nc\n||d\n&&\ne) e = f', 'if (a &&\n b ||\n c ||\n d &&\n e) e = f');
bt('if(a &&\n(b\n||\nc\n||d)\n&&\ne) e = f', 'if (a &&\n (b ||\n c ||\n d) &&\n e) e = f');
test_fragment('\n\n"x"', '"x"');
// this beavior differs between js and python, defaults to unlimited in js, 10 in python

View File

@ -772,7 +772,7 @@ class TestJSBeautifier(unittest.TestCase):
# Line wrap test intputs
#..............---------1---------2---------3---------4---------5---------6---------7
#..............1234567890123456789012345678901234567890123456789012345678901234567890
wrap_input_1=('foo.bar().baz().cucumber((fat && "sassy") || (leans\n&& mean));\n' +
wrap_input_1=('foo.bar().baz().cucumber((fat && "sassy") || (leans && mean));\n' +
'Test_very_long_variable_name_this_should_never_wrap\n.but_this_can\n' +
'return between_return_and_expression_should_never_wrap.but_this_can\n' +
'throw between_throw_and_expression_should_never_wrap.but_this_can\n' +
@ -787,7 +787,7 @@ class TestJSBeautifier(unittest.TestCase):
#..............---------1---------2---------3---------4---------5---------6---------7
#..............1234567890123456789012345678901234567890123456789012345678901234567890
wrap_input_2=('{\n' +
' foo.bar().baz().cucumber((fat && "sassy") || (leans\n&& mean));\n' +
' foo.bar().baz().cucumber((fat && "sassy") || (leans && mean));\n' +
' Test_very_long_variable_name_this_should_never_wrap\n.but_this_can\n' +
' return between_return_and_expression_should_never_wrap.but_this_can\n' +
' throw between_throw_and_expression_should_never_wrap.but_this_can\n' +
@ -1207,8 +1207,8 @@ class TestJSBeautifier(unittest.TestCase):
bt('var a = /*i*/\n"b";', 'var a = /*i*/\n "b";')
bt('var a = /*i*/\nb;', 'var a = /*i*/\n b;')
bt('{\n\n\n"x"\n}', '{\n\n\n "x"\n}')
bt('if(a &&\nb\n||\nc\n||d\n&&\ne) e = f', 'if (a &&\n b ||\n c || d &&\n e) e = f')
bt('if(a &&\n(b\n||\nc\n||d)\n&&\ne) e = f', 'if (a &&\n (b ||\n c || d) &&\n e) e = f')
bt('if(a &&\nb\n||\nc\n||d\n&&\ne) e = f', 'if (a &&\n b ||\n c ||\n d &&\n e) e = f')
bt('if(a &&\n(b\n||\nc\n||d)\n&&\ne) e = f', 'if (a &&\n (b ||\n c ||\n d) &&\n e) e = f')
test_fragment('\n\n"x"', '"x"')
# this beavior differs between js and python, defaults to unlimited in js, 10 in python
bt('a = 1;\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nb = 2;',

View File

@ -1,3 +1,5 @@
var inputlib = require('./inputlib');
exports.test_data = {
default_options: [
{ name: "indent_size", value: "4" },
@ -5,7 +7,8 @@ exports.test_data = {
{ name: "preserve_newlines", value: "true" },
{ name: "jslint_happy", value: "false" },
{ name: "keep_array_indentation", value: "false" },
{ name: "brace_style", value: "'collapse'" }
{ name: "brace_style", value: "'collapse'" },
{ name: "operator_position", value: "'before-newline'" }
],
groups: [{
name: "Unicode Support",
@ -315,6 +318,233 @@ exports.test_data = {
],
},
],
}, {
name: "operator_position option - ensure no neswlines if preserve_newlines is false",
matrix: [
{
options: [
{ name: "operator_position", value: "'before-newline'" },
{ name: "preserve_newlines", value: "false" }
]
}, {
options: [
{ name: "operator_position", value: "'after-newline'" },
{ name: "preserve_newlines", value: "false" }
]
}, {
options: [
{ name: "operator_position", value: "'preserve-newline'" },
{ name: "preserve_newlines", value: "false" }
]
}
],
tests: [
{
unchanged: inputlib.operator_position.sanity
}, {
input: inputlib.operator_position.comprehensive,
output: inputlib.operator_position.sanity,
}
]
}, {
name: "operator_position option - set to 'before-newline' (default value)",
tests: [
{
comment: 'comprehensive, various newlines',
input: inputlib.operator_position.comprehensive,
output: [
'var res = a + b -',
' c /',
' d * e %',
' f;',
'var res = g & h |',
' i ^',
' j;',
'var res = (k &&',
' l ||',
' m) ?',
' n :',
' o;',
'var res = p >>',
' q <<',
' r >>>',
' s;',
'var res = t',
'',
' ===',
' u !== v !=',
' w ==',
' x >=',
' y <= z > aa <',
' ab;',
'ac +',
' -ad'
]
}, {
comment: 'colon special case',
input: inputlib.operator_position.colon_special_case,
output: [
'var a = {',
' b: bval,',
' c: cval,',
' d: dval',
'};',
'var e = f ? g :',
' h;',
'var i = j ? k :',
' l;'
]
}, {
comment: 'catch-all, includes brackets and other various code',
input: inputlib.operator_position.catch_all,
output: [
'var d = 1;',
'if (a === b &&',
' c) {',
' d = (c * everything /',
' something_else) %',
' b;',
' e',
' += d;',
'',
'} else if (!(complex && simple) ||',
' (emotion && emotion.name === "happy")) {',
' cryTearsOfJoy(many ||',
' anOcean ||',
' aRiver);',
'}'
]
}
]
}, {
name: "operator_position option - set to 'after_newline'",
options: [{
name: "operator_position", value: "'after-newline'"
}],
tests: [
{
comment: 'comprehensive, various newlines',
input: inputlib.operator_position.comprehensive,
output: [
'var res = a + b',
' - c',
' / d * e',
' % f;',
'var res = g & h',
' | i',
' ^ j;',
'var res = (k',
' && l',
' || m)',
' ? n',
' : o;',
'var res = p',
' >> q',
' << r',
' >>> s;',
'var res = t',
'',
' === u !== v',
' != w',
' == x',
' >= y <= z > aa',
' < ab;',
'ac',
' + -ad'
]
}, {
comment: 'colon special case',
input: inputlib.operator_position.colon_special_case,
output: [
'var a = {',
' b: bval,',
' c: cval,',
' d: dval',
'};',
'var e = f ? g',
' : h;',
'var i = j ? k',
' : l;'
]
}, {
comment: 'catch-all, includes brackets and other various code',
input: inputlib.operator_position.catch_all,
output: [
'var d = 1;',
'if (a === b',
' && c) {',
' d = (c * everything',
' / something_else)',
' % b;',
' e',
' += d;',
'',
'} else if (!(complex && simple)',
' || (emotion && emotion.name === "happy")) {',
' cryTearsOfJoy(many',
' || anOcean',
' || aRiver);',
'}'
]
}
]
}, {
name: "operator_position option - set to 'preserve-newline'",
options: [{
name: "operator_position", value: "'preserve-newline'"
}],
tests: [
{
comment: 'comprehensive, various newlines',
input: inputlib.operator_position.comprehensive,
output: [
'var res = a + b',
' - c /',
' d * e',
' %',
' f;',
'var res = g & h',
' | i ^',
' j;',
'var res = (k &&',
' l',
' || m) ?',
' n',
' : o;',
'var res = p',
' >> q <<',
' r',
' >>> s;',
'var res = t',
'',
' === u !== v',
' !=',
' w',
' == x >=',
' y <= z > aa <',
' ab;',
'ac +',
' -ad'
]
}, {
comment: 'colon special case',
input: inputlib.operator_position.colon_special_case,
output: [
'var a = {',
' b: bval,',
' c: cval,',
' d: dval',
'};',
'var e = f ? g',
' : h;',
'var i = j ? k :',
' l;'
]
}, {
comment: 'catch-all, includes brackets and other various code',
unchanged: inputlib.operator_position.catch_all
}
]
}, {
name: "New Test Suite"
},