Add "end_with_newline" option throughout and make consistent

-n, --end_with_newline added to all beautifiers

This changes to the way the command-line produces output.
Piped output and file output is now the same, and by default will not add a newline at the end.
Ending with newline is respected when passed via html beautifier to css or js beautifier.

Fixing this also exposed a number of holes tests and test framework.

Fixes #492
This commit is contained in:
Liam Newman 2014-09-28 02:45:00 -07:00
parent 5b2736f096
commit d030e1cbed
13 changed files with 214 additions and 112 deletions

View File

@ -170,6 +170,7 @@
$('#wrap-line-length').val(any($.cookie('wrap-line-length'), '0'));
$('#unescape-strings').prop('checked', $.cookie('unescape-strings') === 'on');
$('#jslint-happy').prop('checked', $.cookie('jslint-happy') === 'on');
$('#end-with-newline').prop('checked', $.cookie('end-with-newline') === 'on');
}
function store_settings_to_cookie() {
@ -185,7 +186,8 @@
$.cookie('space-before-conditional', $('#space-before-conditional').prop('checked') ? 'on' : 'off',
opts);
$.cookie('unescape-strings', $('#unescape-strings').prop('checked') ? 'on' : 'off', opts);
$.cookie('jslint-happy', $('jslint-happy').prop('checked') ? 'on' : 'off', opts);
$.cookie('jslint-happy', $('#jslint-happy').prop('checked') ? 'on' : 'off', opts);
$.cookie('end-with-newline', $('#end-with-newline').prop('checked') ? 'on' : 'off', opts);
$.cookie('wrap-line-length', $('#wrap-line-length').val(), opts);
$.cookie('indent-scripts', $('#indent-scripts').val(), opts);
}
@ -248,6 +250,7 @@
opts.space_before_conditional = $('#space-before-conditional').prop('checked');
opts.unescape_strings = $('#unescape-strings').prop('checked');
opts.jslint_happy = $('#jslint-happy').prop('checked');
opts.end_with_newline = $('#end-with-newline').prop('checked');
opts.wrap_line_length = $('#wrap-line-length').val();
opts.space_after_anon_function = true;
@ -340,7 +343,9 @@
</td>
<td>
<input class="checkbox" type="checkbox" id="end-with-newline">
<label for="end-with-newline">End script and style with newline?</label>
<br>
<input class="checkbox" type="checkbox" id="detect-packers">
<label for="detect-packers">Detect packers and obfuscators?</label>
<br>

View File

@ -1,4 +1,4 @@
#!/usr/bin/env node
var cli = require('../lib/cli');
cli.interpret();
cli.interpret();

View File

@ -64,7 +64,7 @@
var indentSize = options.indent_size || 4;
var indentCharacter = options.indent_char || ' ';
var selectorSeparatorNewline = (options.selector_separator_newline === undefined) ? true : options.selector_separator_newline;
var endWithNewline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
var end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
// compatibility
if (typeof indentSize === "string") {
@ -336,8 +336,7 @@
var sweetCode = output.join('').replace(/[\r\n\t ]+$/, '');
// establish end_with_newline
var should = endWithNewline;
if (should) {
if (end_with_newline) {
sweetCode += "\n";
}

View File

@ -52,6 +52,8 @@
Only works before elements, not inside tags or for text.
max_preserve_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk
indent_handlebars (default false) - format and indent {{#foo}} and {{/foo}}
end_with_newline (false) - end with a newline
e.g.
@ -90,7 +92,8 @@
unformatted,
preserve_newlines,
max_preserve_newlines,
indent_handlebars;
indent_handlebars,
end_with_newline;
options = options || {};
@ -111,6 +114,7 @@
(isNaN(parseInt(options.max_preserve_newlines, 10)) ? 32786 : parseInt(options.max_preserve_newlines, 10))
: 0;
indent_handlebars = (options.indent_handlebars === undefined) ? false : options.indent_handlebars;
end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
function Parser() {
@ -801,8 +805,8 @@
.replace(/\s+$/, '');
}
if (text) {
multi_parser.print_token_raw(indentation + trim(text));
multi_parser.print_newline(false, multi_parser.output);
multi_parser.print_token_raw(text);
multi_parser.print_newline(true, multi_parser.output);
}
}
multi_parser.current_mode = 'TAG';
@ -811,7 +815,11 @@
multi_parser.last_token = multi_parser.token_type;
multi_parser.last_text = multi_parser.token_text;
}
return multi_parser.output.join('');
var sweet_code = multi_parser.output.join('').replace(/[\r\n\t ]+$/, '');
if (end_with_newline) {
sweet_code += '\n';
}
return sweet_code;
}
if (typeof define === "function" && define.amd) {

View File

@ -72,6 +72,9 @@
NOTE: This is not a hard limit. Lines will continue until a point where a newline would
be preserved if it were present.
end_with_newline (default false) - end output with a newline
e.g
js_beautify(js_source_text, {
@ -267,6 +270,8 @@
opt.unescape_strings = (options.unescape_strings === undefined) ? false : options.unescape_strings;
opt.wrap_line_length = (options.wrap_line_length === undefined) ? 0 : parseInt(options.wrap_line_length, 10);
opt.e4x = (options.e4x === undefined) ? false : options.e4x;
opt.end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
// force opt.space_after_anon_function to true if opt.jslint_happy
if(opt.jslint_happy) {
@ -338,6 +343,10 @@
}
sweet_code = output.get_code();
if (opt.end_with_newline) {
sweet_code += '\n';
}
return sweet_code;
};
@ -1320,7 +1329,7 @@
this.add_space_before_token = function() {
if (this.space_before_token && this.current_line.get_item_count()) {
var last_output = this.current_line.last();
if (last_output !== ' ' && last_output !== indent_string) { // prevent occassional duplicate space
if (last_output !== ' ' && last_output !== indent_string && last_output !== baseIndentString) { // prevent occassional duplicate space
this.current_line.push(' ');
}
}

View File

@ -61,6 +61,7 @@ var fs = require('fs'),
"unescape_strings": Boolean,
"wrap_line_length": Number,
"e4x": Boolean,
"end_with_newline": Boolean,
// HTML-only
"max_char": Number, // obsolete since 1.3.5
"unformatted": [String, Array],
@ -96,6 +97,7 @@ var fs = require('fs'),
"x": ["--unescape_strings"],
"w": ["--wrap_line_length"],
"X": ["--e4x"],
"n": ["--end_with_newline"],
// HTML-only
"W": ["--max_char"], // obsolete since 1.3.5
"U": ["--unformatted"],
@ -222,6 +224,7 @@ function usage(err) {
msg.push(' -w, --wrap-line-length Wrap lines at next opportunity after N characters [0]');
msg.push(' -X, --e4x Pass E4X xml literals through untouched');
msg.push(' --good-stuff Warm the cockles of Crockford\'s heart');
msg.push(' -n, --end_with_newline End output with newline');
break;
case "html":
msg.push(' -b, --brace-style [collapse|expand|end-expand] ["collapse"]');
@ -281,11 +284,6 @@ function makePretty(code, config, outfile, callback) {
var fileType = getOutputType(outfile, config.type);
var pretty = beautify[fileType](code, config);
// ensure newline at end of beautified output
if (pretty && pretty.charAt(pretty.length - 1) !== '\n') {
pretty += '\n';
}
callback(null, pretty, outfile, config);
} catch (ex) {
callback(ex);

View File

@ -13,7 +13,7 @@ function run_beautifier_tests(test_obj, Urlencoded, js_beautify, html_beautify,
space_before_conditional: true,
break_chained_methods: false,
selector_separator: '\n',
end_with_newline: true
end_with_newline: false
};
function test_js_beautifier(input)
@ -37,11 +37,11 @@ function run_beautifier_tests(test_obj, Urlencoded, js_beautify, html_beautify,
// does not check the indentation / surroundings as bt() does
function test_fragment(input, expected)
{
expected = expected || input;
expected = expected || expected === '' ? expected : input;
sanitytest.expect(input, expected);
// if the expected is different from input, run it again
// expected output should be unchanged when run twice.
if (expected != input) {
if (expected !== input) {
sanitytest.expect(expected, expected);
}
}
@ -54,7 +54,7 @@ function run_beautifier_tests(test_obj, Urlencoded, js_beautify, html_beautify,
{
var wrapped_input, wrapped_expectation;
expectation = expectation || input;
expectation = expectation || expectation === '' ? expectation : input;
sanitytest.test_function(test_js_beautifier, 'js_beautify');
test_fragment(input, expectation);
@ -79,13 +79,16 @@ function run_beautifier_tests(test_obj, Urlencoded, js_beautify, html_beautify,
{
var wrapped_input, wrapped_expectation, field_input, field_expectation;
expectation = expectation || input;
expectation = expectation || expectation === '' ? expectation : input;
sanitytest.test_function(test_html_beautifier, 'html_beautify');
test_fragment(input, expectation);
if (opts.indent_size === 4 && input) {
wrapped_input = '<div>\n' + input.replace(/^(.+)$/mg, ' $1') + '\n <span>inline</span>\n</div>';
wrapped_expectation = '<div>\n' + expectation.replace(/^(.+)$/mg, ' $1') + '\n <span>inline</span>\n</div>';
if (opts.end_with_newline) {
wrapped_expectation += '\n';
}
test_fragment(wrapped_input, wrapped_expectation);
}
@ -114,7 +117,7 @@ function run_beautifier_tests(test_obj, Urlencoded, js_beautify, html_beautify,
{
var wrapped_input, wrapped_expectation;
expectation = expectation || input;
expectation = expectation || expectation === '' ? expectation : input;
sanitytest.test_function(test_css_beautifier, 'css_beautify');
test_fragment(input, expectation);
}
@ -140,15 +143,23 @@ function run_beautifier_tests(test_obj, Urlencoded, js_beautify, html_beautify,
opts.keep_array_indentation = false;
opts.brace_style = "collapse";
// unicode support
bt('var ' + String.fromCharCode(3232) + '_' + String.fromCharCode(3232) + ' = "hi";');
bt('var ' + String.fromCharCode(228) + 'x = {\n ' + String.fromCharCode(228) + 'rgerlich: true\n};');
opts.end_with_newline = true;
test_fragment('', '\n');
test_fragment(' return .5',' return .5\n');
test_fragment(' \n\nreturn .5\n\n\n\n',' return .5\n');
test_fragment('\n', '\n');
opts.end_with_newline = false;
bt('');
test_fragment('\n', '');
bt('return .5');
test_fragment(' return .5');
test_fragment(' return .5;\n a();');
test_fragment(' < div');
bt('a = 1', 'a = 1');
bt('a=1', 'a = 1');
bt("a();\n\nb();", "a();\n\nb();");
@ -1786,7 +1797,29 @@ function run_beautifier_tests(test_obj, Urlencoded, js_beautify, html_beautify,
'var a = {\n bing: 1\n },\n b = 2,\n c = 3;');
Urlencoded.run_tests(sanitytest);
bth('');
opts.end_with_newline = true;
test_fragment('', '\n');
test_fragment('<div></div>\n');
test_fragment('<div></div>\n\n\n', '<div></div>\n');
test_fragment('<head>\n' +
' <script>\n' +
' mocha.setup("bdd");\n' +
'\n' +
' </script>\n' +
'</head>\n');
opts.end_with_newline = false;
test_fragment('<head>\n' +
' <script>\n' +
' mocha.setup("bdd");\n' +
' </script>\n' +
'</head>');
test_fragment('<div></div>\n', '<div></div>');
bth('<div></div>');
bth('<div>content</div>');
bth('<div><div></div></div>',
@ -2164,45 +2197,53 @@ function run_beautifier_tests(test_obj, Urlencoded, js_beautify, html_beautify,
opts.indent_char = '\t';
opts.selector_separator_newline = true;
opts.end_with_newline = true;
// test basic css beautifier
btc('', '\n');
btc('\n', '\n');
btc(".tabs{}\n", ".tabs {}\n");
btc(".tabs{}", ".tabs {}\n");
btc(".tabs{color:red;}", ".tabs {\n\tcolor: red;\n}\n");
btc(".tabs{color:rgb(255, 255, 0)}", ".tabs {\n\tcolor: rgb(255, 255, 0)\n}\n");
btc(".tabs{background:url('back.jpg')}", ".tabs {\n\tbackground: url('back.jpg')\n}\n");
btc("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}\n");
btc("@media print {.tab{}}", "@media print {\n\t.tab {}\n}\n");
btc("@media print {.tab{background-image:url(foo@2x.png)}}", "@media print {\n\t.tab {\n\t\tbackground-image: url(foo@2x.png)\n\t}\n}\n");
opts.end_with_newline = false;
btc('', '');
btc('\n', '');
btc(".tabs{}\n", ".tabs {}");
// test basic css beautifier
btc(".tabs {}");
btc(".tabs{color:red;}", ".tabs {\n\tcolor: red;\n}");
btc(".tabs{color:rgb(255, 255, 0)}", ".tabs {\n\tcolor: rgb(255, 255, 0)\n}");
btc(".tabs{background:url('back.jpg')}", ".tabs {\n\tbackground: url('back.jpg')\n}");
btc("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}");
btc("@media print {.tab{}}", "@media print {\n\t.tab {}\n}");
btc("@media print {.tab{background-image:url(foo@2x.png)}}", "@media print {\n\t.tab {\n\t\tbackground-image: url(foo@2x.png)\n\t}\n}");
//lead-in whitespace determines base-indent.
// lead-in newlines are stripped.
btc("\n\na, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}\n");
btc(" a, img {padding: 0.2px}", " a,\n img {\n \tpadding: 0.2px\n }\n");
btc(" \t \na, img {padding: 0.2px}", " \t a,\n \t img {\n \t \tpadding: 0.2px\n \t }\n");
btc("\n\n a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}\n");
btc("\n\na, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}");
btc(" a, img {padding: 0.2px}", " a,\n img {\n \tpadding: 0.2px\n }");
btc(" \t \na, img {padding: 0.2px}", " \t a,\n \t img {\n \t \tpadding: 0.2px\n \t }");
btc("\n\n a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}");
// comments
btc("/* test */", "/* test */\n");
btc(".tabs{/* test */}", ".tabs {\n\t/* test */\n}\n");
btc("/* header */.tabs {}", "/* header */\n\n.tabs {}\n");
btc("/* test */", "/* test */");
btc(".tabs{/* test */}", ".tabs {\n\t/* test */\n}");
btc("/* header */.tabs {}", "/* header */\n\n.tabs {}");
//single line comment support (less/sass)
btc(".tabs{\n// comment\nwidth:10px;\n}", ".tabs {\n\t// comment\n\twidth: 10px;\n}\n");
btc(".tabs{// comment\nwidth:10px;\n}", ".tabs {\n\t// comment\n\twidth: 10px;\n}\n");
btc("//comment\n.tabs{width:10px;}", "//comment\n.tabs {\n\twidth: 10px;\n}\n");
btc(".tabs{//comment\n//2nd single line comment\nwidth:10px;}", ".tabs {\n\t//comment\n\t//2nd single line comment\n\twidth: 10px;\n}\n");
btc(".tabs{width:10px;//end of line comment\n}", ".tabs {\n\twidth: 10px;//end of line comment\n}\n");
btc(".tabs{width:10px;//end of line comment\nheight:10px;}", ".tabs {\n\twidth: 10px;//end of line comment\n\theight: 10px;\n}\n");
btc(".tabs{width:10px;//end of line comment\nheight:10px;//another\n}", ".tabs {\n\twidth: 10px;//end of line comment\n\theight: 10px;//another\n}\n");
btc(".tabs{\n// comment\nwidth:10px;\n}", ".tabs {\n\t// comment\n\twidth: 10px;\n}");
btc(".tabs{// comment\nwidth:10px;\n}", ".tabs {\n\t// comment\n\twidth: 10px;\n}");
btc("//comment\n.tabs{width:10px;}", "//comment\n.tabs {\n\twidth: 10px;\n}");
btc(".tabs{//comment\n//2nd single line comment\nwidth:10px;}", ".tabs {\n\t//comment\n\t//2nd single line comment\n\twidth: 10px;\n}");
btc(".tabs{width:10px;//end of line comment\n}", ".tabs {\n\twidth: 10px;//end of line comment\n}");
btc(".tabs{width:10px;//end of line comment\nheight:10px;}", ".tabs {\n\twidth: 10px;//end of line comment\n\theight: 10px;\n}");
btc(".tabs{width:10px;//end of line comment\nheight:10px;//another\n}", ".tabs {\n\twidth: 10px;//end of line comment\n\theight: 10px;//another\n}");
// separate selectors
btc("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}\n");
btc("a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}\n");
btc("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}");
btc("a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}");
// block nesting
btc("#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: 'Bitstream Vera Serif Bold';\n\t\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n\t}\n}\n");
btc("@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: 'Bitstream Vera Serif Bold';\n\t\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n\t}\n}\n");
btc("#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: 'Bitstream Vera Serif Bold';\n\t\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n\t}\n}");
btc("@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: 'Bitstream Vera Serif Bold';\n\t\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n\t}\n}");
/*
@font-face {
font-family: 'Bitstream Vera Serif Bold';
@ -2222,33 +2263,33 @@ function run_beautifier_tests(test_obj, Urlencoded, js_beautify, html_beautify,
}
}
*/
btc("@font-face {\n\tfont-family: 'Bitstream Vera Serif Bold';\n\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n}\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: 'Helvetica Neue'\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}\n");
btc("@font-face {\n\tfont-family: 'Bitstream Vera Serif Bold';\n\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n}\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: 'Helvetica Neue'\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}");
// test options
opts.indent_size = 2;
opts.indent_char = ' ';
opts.selector_separator_newline = false;
btc("#bla, #foo{color:green}", "#bla, #foo {\n color: green\n}\n");
btc("@media print {.tab{}}", "@media print {\n .tab {}\n}\n");
btc("@media print {.tab,.bat{}}", "@media print {\n .tab, .bat {}\n}\n");
btc("#bla, #foo{color:black}", "#bla, #foo {\n color: black\n}\n");
btc("#bla, #foo{color:green}", "#bla, #foo {\n color: green\n}");
btc("@media print {.tab{}}", "@media print {\n .tab {}\n}");
btc("@media print {.tab,.bat{}}", "@media print {\n .tab, .bat {}\n}");
btc("#bla, #foo{color:black}", "#bla, #foo {\n color: black\n}");
// pseudo-classes and pseudo-elements
btc("#foo:hover {\n background-image: url(foo@2x.png)\n}\n");
btc("#foo *:hover {\n color: purple\n}\n");
btc("::selection {\n color: #ff0000;\n}\n");
btc("#foo:hover {\n background-image: url(foo@2x.png)\n}");
btc("#foo *:hover {\n color: purple\n}");
btc("::selection {\n color: #ff0000;\n}");
// TODO: don't break nested pseduo-classes
btc("@media screen {.tab,.bat:hover {color:red}}", "@media screen {\n .tab, .bat:hover {\n color: red\n }\n}\n");
btc("@media screen {.tab,.bat:hover {color:red}}", "@media screen {\n .tab, .bat:hover {\n color: red\n }\n}");
// particular edge case with braces and semicolons inside tags that allows custom text
btc("a:not(\"foobar\\\";{}omg\"){\ncontent: 'example\\';{} text';\ncontent: \"example\\\";{} text\";}",
"a:not(\"foobar\\\";{}omg\") {\n content: 'example\\';{} text';\n content: \"example\\\";{} text\";\n}\n");
"a:not(\"foobar\\\";{}omg\") {\n content: 'example\\';{} text';\n content: \"example\\\";{} text\";\n}");
// may not eat the space before "["
btc('html.js [data-custom="123"] {\n opacity: 1.00;\n}\n');
btc('html.js *[data-custom="123"] {\n opacity: 1.00;\n}\n');
btc('html.js [data-custom="123"] {\n opacity: 1.00;\n}');
btc('html.js *[data-custom="123"] {\n opacity: 1.00;\n}');
return sanitytest;
}

View File

@ -22,8 +22,8 @@ test_cli_common()
exit 1
}
$CLI_SCRIPT -invalidParameter 2> /dev/null && {
echo "[$CLI_SCRIPT_NAME -invalidParameter] Return code should be error."
$CLI_SCRIPT -Z 2> /dev/null && {
echo "[$CLI_SCRIPT_NAME -Z] Return code for invalid parameter should be error."
exit 1
}

View File

@ -304,9 +304,8 @@ class Beautifier:
sweet_code = re.sub('[\r\n\t ]+$', '', printer.result())
# establish end_with_newline
should = self.opts.end_with_newline
if should:
sweet_code = sweet_code + "\n"
if self.opts.end_with_newline:
sweet_code += "\n"
return sweet_code

View File

@ -9,55 +9,67 @@ class CSSBeautifierTest(unittest.TestCase):
self.options.indent_size = 1
self.options.indent_char = '\t'
self.options.selector_separator_newline = True
self.options.end_with_newline = True
self.options.end_with_newline = False
def testNewline(self):
self.resetOptions()
t = self.decodesto
self.options.end_with_newline = True
t("", "\n")
t("\n", "\n")
t(".tabs{}\n", ".tabs {}\n")
t(".tabs{}", ".tabs {}\n")
def testBasics(self):
self.resetOptions()
t = self.decodesto
t("", "\n")
t(".tabs{}", ".tabs {}\n")
t(".tabs{color:red}", ".tabs {\n\tcolor: red\n}\n")
t(".tabs{color:rgb(255, 255, 0)}", ".tabs {\n\tcolor: rgb(255, 255, 0)\n}\n")
t(".tabs{background:url('back.jpg')}", ".tabs {\n\tbackground: url('back.jpg')\n}\n")
t("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}\n")
t("@media print {.tab{}}", "@media print {\n\t.tab {}\n}\n")
t("", "")
t("\n", "")
t(".tabs{}\n", ".tabs {}")
t(".tabs{}", ".tabs {}")
t(".tabs{color:red}", ".tabs {\n\tcolor: red\n}")
t(".tabs{color:rgb(255, 255, 0)}", ".tabs {\n\tcolor: rgb(255, 255, 0)\n}")
t(".tabs{background:url('back.jpg')}", ".tabs {\n\tbackground: url('back.jpg')\n}")
t("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}")
t("@media print {.tab{}}", "@media print {\n\t.tab {}\n}")
# may not eat the space before "["
t('html.js [data-custom="123"] {\n\topacity: 1.00;\n}\n');
t('html.js *[data-custom="123"] {\n\topacity: 1.00;\n}\n');
t('html.js [data-custom="123"] {\n\topacity: 1.00;\n}')
t('html.js *[data-custom="123"] {\n\topacity: 1.00;\n}')
# lead-in whitespace determines base-indent.
# lead-in newlines are stripped.
t("\n\na, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}\n")
t(" a, img {padding: 0.2px}", " a,\n img {\n \tpadding: 0.2px\n }\n")
t(" \t \na, img {padding: 0.2px}", " \t a,\n \t img {\n \t \tpadding: 0.2px\n \t }\n")
t("\n\n a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}\n")
t("\n\na, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}")
t(" a, img {padding: 0.2px}", " a,\n img {\n \tpadding: 0.2px\n }")
t(" \t \na, img {padding: 0.2px}", " \t a,\n \t img {\n \t \tpadding: 0.2px\n \t }")
t("\n\n a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}")
def testComments(self):
self.resetOptions()
t = self.decodesto
t("/* test */", "/* test */\n")
t(".tabs{/* test */}", ".tabs {\n\t/* test */\n}\n")
t("/* header */.tabs {}", "/* header */\n\n.tabs {}\n")
t("/* test */", "/* test */")
t(".tabs{/* test */}", ".tabs {\n\t/* test */\n}")
t("/* header */.tabs {}", "/* header */\n\n.tabs {}")
#single line comment support (less/sass)
t(".tabs{\n// comment\nwidth:10px;\n}", ".tabs {\n\t// comment\n\twidth: 10px;\n}\n")
t(".tabs{// comment\nwidth:10px;\n}", ".tabs {\n\t// comment\n\twidth: 10px;\n}\n")
t("//comment\n.tabs{width:10px;}", "//comment\n.tabs {\n\twidth: 10px;\n}\n")
t(".tabs{//comment\n//2nd single line comment\nwidth:10px;}", ".tabs {\n\t//comment\n\t//2nd single line comment\n\twidth: 10px;\n}\n")
t(".tabs{width:10px;//end of line comment\n}", ".tabs {\n\twidth: 10px;//end of line comment\n}\n")
t(".tabs{width:10px;//end of line comment\nheight:10px;}", ".tabs {\n\twidth: 10px;//end of line comment\n\theight: 10px;\n}\n")
t(".tabs{width:10px;//end of line comment\nheight:10px;//another\n}", ".tabs {\n\twidth: 10px;//end of line comment\n\theight: 10px;//another\n}\n")
t(".tabs{\n// comment\nwidth:10px;\n}", ".tabs {\n\t// comment\n\twidth: 10px;\n}")
t(".tabs{// comment\nwidth:10px;\n}", ".tabs {\n\t// comment\n\twidth: 10px;\n}")
t("//comment\n.tabs{width:10px;}", "//comment\n.tabs {\n\twidth: 10px;\n}")
t(".tabs{//comment\n//2nd single line comment\nwidth:10px;}", ".tabs {\n\t//comment\n\t//2nd single line comment\n\twidth: 10px;\n}")
t(".tabs{width:10px;//end of line comment\n}", ".tabs {\n\twidth: 10px;//end of line comment\n}")
t(".tabs{width:10px;//end of line comment\nheight:10px;}", ".tabs {\n\twidth: 10px;//end of line comment\n\theight: 10px;\n}")
t(".tabs{width:10px;//end of line comment\nheight:10px;//another\n}", ".tabs {\n\twidth: 10px;//end of line comment\n\theight: 10px;//another\n}")
def testSeperateSelectors(self):
self.resetOptions()
t = self.decodesto
t("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}\n")
t("a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}\n")
t("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}")
t("a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}")
def testOptions(self):
self.resetOptions()
@ -66,13 +78,22 @@ class CSSBeautifierTest(unittest.TestCase):
self.options.selector_separator_newline = False
t = self.decodesto
t("#bla, #foo{color:green}", "#bla, #foo {\n color: green\n}\n")
t("@media print {.tab{}}", "@media print {\n .tab {}\n}\n")
t("#bla, #foo{color:black}", "#bla, #foo {\n color: black\n}\n")
t("#bla, #foo{color:green}", "#bla, #foo {\n color: green\n}")
t("@media print {.tab{}}", "@media print {\n .tab {}\n}")
t("#bla, #foo{color:black}", "#bla, #foo {\n color: black\n}")
def decodesto(self, input, expectation=None):
self.assertMultiLineEqual(
cssbeautifier.beautify(input, self.options), expectation or input)
if expectation == None:
expectation = input
self.assertMultiLineEqual(
cssbeautifier.beautify(input, self.options), expectation)
# if the expected is different from input, run it again
# expected output should be unchanged when run twice.
if not expectation == None:
self.assertMultiLineEqual(
cssbeautifier.beautify(expectation, self.options), expectation)
if __name__ == '__main__':
unittest.main()

View File

@ -78,6 +78,7 @@ class BeautifierOptions:
self.unescape_strings = False
self.wrap_line_length = 0
self.break_chained_methods = False
self.end_with_newline = False
@ -272,6 +273,7 @@ Output options:
-X, --e4x Pass E4X xml literals through untouched
-w, --wrap-line-length Attempt to wrap line when it exceeds this length.
NOTE: Line continues until next wrap point is found.
-n, --end_with_newline End output with newline
Rarely needed options:
@ -393,6 +395,9 @@ class Beautifier:
sweet_code = self.output.get_code()
if self.opts.end_with_newline:
sweet_code += "\n"
return sweet_code
def handle_token(self, local_token):
@ -1205,7 +1210,7 @@ class Output:
for line_index in range(1, len(self.lines)):
sweet_code += '\n' + self.lines[line_index].get_output()
return re.sub('[\n ]+$', '', sweet_code)
return re.sub('[\r\n\t ]+$', '', sweet_code)
def add_indent_string(self, level):
if self.baseIndentString != '':
@ -1226,7 +1231,7 @@ class Output:
def add_space_before_token(self):
# make sure only single space gets drawn
if self.space_before_token and self.current_line.get_item_count() and \
self.current_line.last() not in [' ', self.indent_string]:
self.current_line.last() not in [' ', self.indent_string, self.baseIndentString]:
self.current_line.push(' ')
self.space_before_token = False
@ -1654,12 +1659,12 @@ def main():
argv = sys.argv[1:]
try:
opts, args = getopt.getopt(argv, "s:c:o:dEPjabkil:xhtfvXw:",
opts, args = getopt.getopt(argv, "s:c:o:dEPjabkil:xhtfvXnw:",
['indent-size=','indent-char=','outfile=', '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', 'wrap-line-length'])
'e4x', 'end-with-newline','wrap-line-length'])
except getopt.GetoptError as ex:
print(ex, file=sys.stderr)
return usage(sys.stderr)
@ -1702,6 +1707,8 @@ def main():
js_options.unescape_strings = True
elif opt in ('--e4x', '-X'):
js_options.e4x = True
elif opt in ('--end-with-newline', '-n'):
js_options.end_with_newline = True
elif opt in ('--wrap-line-length ', '-w'):
js_options.wrap_line_length = int(arg)
elif opt in ('--stdin', '-i'):
@ -1718,11 +1725,11 @@ def main():
else:
try:
if outfile == 'stdout':
print(beautify_file(file, js_options))
sys.stdout.write(beautify_file(file, js_options))
else:
mkdir_p(os.path.dirname(outfile))
with open(outfile, 'w') as f:
f.write(beautify_file(file, js_options) + '\n')
f.write(beautify_file(file, js_options))
except Exception as ex:
print(ex, file=sys.stderr)
return 1

View File

@ -22,8 +22,8 @@ test_cli_common()
exit 1
}
$CLI_SCRIPT -invalidParameter 2> /dev/null && {
echo "[$CLI_SCRIPT_NAME -invalidParameter] Return code should be error."
$CLI_SCRIPT -Z 2> /dev/null && {
echo "[$CLI_SCRIPT_NAME -Z] Return code for invalid parameter should be error."
exit 1
}
@ -68,23 +68,23 @@ test_cli_js_beautify()
}
$CLI_SCRIPT $SCRIPT_DIR/../../../js/bin/css-beautify.js > /dev/null || {
echo "js-beautify output for $SCRIPT_DIR/../bin/css-beautify.js was expected succeed."
echo "js-beautify output for $SCRIPT_DIR/../../../js/bin/css-beautify.js was expected succeed."
exit 1
}
$CLI_SCRIPT $SCRIPT_DIR/../../../js/bin/js-beautify.js | diff $SCRIPT_DIR/../../../js/bin/js-beautify.js - || {
echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js was expected to be unchanged."
echo "js-beautify output for $SCRIPT_DIR/../../../js/bin/js-beautify.js was expected to be unchanged."
exit 1
}
rm -rf /tmp/js-beautify-mkdir
$CLI_SCRIPT -o /tmp/js-beautify-mkdir/js-beautify.js $SCRIPT_DIR/../../../js/bin/js-beautify.js && diff $SCRIPT_DIR/../../../js/bin/js-beautify.js /tmp/js-beautify-mkdir/js-beautify.js || {
echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js should have been created in /tmp/js-beautify-mkdir/js-beautify.js."
echo "js-beautify output for $SCRIPT_DIR/../../../js/bin/js-beautify.js should have been created in /tmp/js-beautify-mkdir/js-beautify.js."
exit 1
}
$CLI_SCRIPT $SCRIPT_DIR/../../../js/bin/css-beautify.js | diff -q $SCRIPT_DIR/../../../js/bin/css-beautify.js - && {
echo "js-beautify output for $SCRIPT_DIR/../bin/css-beautify.js was expected to be different."
echo "js-beautify output for $SCRIPT_DIR/../../../js/bin/css-beautify.js was expected to be different."
exit 1
}

View File

@ -43,9 +43,19 @@ class TestJSBeautifier(unittest.TestCase):
bt('var ' + six.unichr(3232) + '_' + six.unichr(3232) + ' = "hi";');
bt('var ' + six.unichr(228) + 'x = {\n ' + six.unichr(228) + 'rgerlich: true\n};');
self.options.end_with_newline = True;
test_fragment('', '\n');
test_fragment(' return .5',' return .5\n');
test_fragment(' \n\nreturn .5\n\n\n\n',' return .5\n');
test_fragment('\n', '\n');
self.options.end_with_newline = False;
bt('');
test_fragment('\n', '');
bt('return .5');
test_fragment(' return .5');
test_fragment(' return .5');
test_fragment(' return .5;\n a();');
test_fragment(' < div');
bt('a = 1', 'a = 1');
bt('a=1', 'a = 1');
bt("a();\n\nb();", "a();\n\nb();");
@ -1658,8 +1668,11 @@ class TestJSBeautifier(unittest.TestCase):
def decodesto(self, input, expectation=None):
if expectation == None:
expectation = input
self.assertMultiLineEqual(
jsbeautifier.beautify(input, self.options), expectation or input)
jsbeautifier.beautify(input, self.options), expectation)
# if the expected is different from input, run it again
# expected output should be unchanged when run twice.
@ -1671,7 +1684,9 @@ class TestJSBeautifier(unittest.TestCase):
return self.wrapregex.sub(' \\1', text)
def bt(self, input, expectation=None):
expectation = expectation or input
if expectation == None:
expectation = input
self.decodesto(input, expectation)
if self.options.indent_size == 4 and input:
wrapped_input = '{\n%s\nfoo=bar;}' % self.wrap(input)