diff --git a/js/lib/beautify.js b/js/lib/beautify.js index b35b4af06..8a7e39453 100644 --- a/js/lib/beautify.js +++ b/js/lib/beautify.js @@ -119,7 +119,11 @@ // Matches a whole line break (where CRLF is considered a single // line break). Used to count lines. - var lineBreak = exports.lineBreak = /\r\n|[\n\r\u2028\u2029]/g; + // in javascript, these two differ + // in python they are the same, different methods are called on them + var lineBreak = exports.lineBreak = /\r\n|[\n\r\u2028\u2029]/; + var allLineBreaks = exports.allLineBreaks = new RegExp(lineBreak.source, 'g'); + // Test whether a given character code starts an identifier. @@ -265,10 +269,9 @@ opt.brace_style = "expand"; } - opt.indent_size = options.indent_size ? parseInt(options.indent_size, 10) : 4; opt.indent_char = options.indent_char ? options.indent_char : ' '; - opt.eol = options.eol ? options.eol : '\n'; + opt.eol = options.eol ? options.eol : 'auto'; opt.preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines; opt.break_chained_methods = (options.break_chained_methods === undefined) ? false : options.break_chained_methods; opt.max_preserve_newlines = (options.max_preserve_newlines === undefined) ? 0 : parseInt(options.max_preserve_newlines, 10); @@ -297,6 +300,13 @@ opt.indent_size = 1; } + if (opt.eol === 'auto') { + opt.eol = '\n'; + if (js_source_text && acorn.lineBreak.test(js_source_text || '')) { + opt.eol = js_source_text.match(acorn.lineBreak)[0]; + } + } + opt.eol = opt.eol.replace(/\\r/, '\r').replace(/\\n/, '\n') //---------------------------------- @@ -402,10 +412,10 @@ // we could use just string.split, but // IE doesn't like returning empty strings - function split_newlines(s) { + function split_linebreaks(s) { //return s.split(/\x0d\x0a|\x0a/); - s = s.replace(/\x0d/g, ''); + s = s.replace(acorn.allLineBreaks, '\n'); var out = [], idx = s.indexOf("\n"); while (idx !== -1) { @@ -1237,7 +1247,7 @@ return; } - var lines = split_newlines(current_token.text); + var lines = split_linebreaks(current_token.text); var j; // iterator for this case var javadoc = false; var starless = false; @@ -1796,7 +1806,7 @@ comment += comment_match[0]; parser_pos += comment_match[0].length; } - comment = comment.replace(acorn.lineBreak, '\n'); + comment = comment.replace(acorn.allLineBreaks, '\n'); return [comment, 'TK_BLOCK_COMMENT', directives]; } // peek for comment // ... @@ -1880,7 +1890,7 @@ var xmlLength = match ? match.index + match[0].length : xmlStr.length; xmlStr = xmlStr.slice(0, xmlLength); parser_pos += xmlLength - 1; - xmlStr = xmlStr.replace(acorn.lineBreak, '\n'); + xmlStr = xmlStr.replace(acorn.allLineBreaks, '\n'); return [xmlStr, "TK_STRING"]; } } else { @@ -1978,7 +1988,7 @@ if(template_match) { c = template_match[0]; parser_pos += c.length - 1; - c = c.replace(acorn.lineBreak, '\n'); + c = c.replace(acorn.allLineBreaks, '\n'); return [c, 'TK_STRING']; } } diff --git a/js/lib/cli.js b/js/lib/cli.js index e55e1d6af..0d5ed0b22 100755 --- a/js/lib/cli.js +++ b/js/lib/cli.js @@ -222,7 +222,8 @@ function usage(err) { ' -s, --indent-size Indentation size [4]', ' -c, --indent-char Indentation character [" "]', ' -t, --indent-with-tabs Indent with tabs, overrides -s and -c', - ' -e, --eol Character(s) to use as line terminators. (default newline - "\\n")', + ' -e, --eol Character(s) to use as line terminators.', + ' [first newline in file, otherwise "\\n]', ' -n, --end-with-newline End output with newline' ]; diff --git a/js/test/shell-smoke-test.sh b/js/test/shell-smoke-test.sh index de86da48b..83d947d92 100755 --- a/js/test/shell-smoke-test.sh +++ b/js/test/shell-smoke-test.sh @@ -83,7 +83,28 @@ test_cli_js_beautify() exit 1 } + # ensure new line settings work + $CLI_SCRIPT -o /tmp/js-beautify-mkdir/js-beautify-n.js --eol '\n' $SCRIPT_DIR/../bin/js-beautify.js + $CLI_SCRIPT -o /tmp/js-beautify-mkdir/js-beautify-rn.js --eol '\r\n' /tmp/js-beautify-mkdir/js-beautify-n.js + + diff -q /tmp/js-beautify-mkdir/js-beautify-n.js /tmp/js-beautify-mkdir/js-beautify-rn.js && { + diff /tmp/js-beautify-mkdir/js-beautify-n.js /tmp/js-beautify-mkdir/js-beautify-rn.js | cat -t -e + echo "js-beautify output for /tmp/js-beautify-mkdir/js-beautify-n.js and /tmp/js-beautify-mkdir/js-beautify-rn.js was expected to be different." + exit 1 + } + + $CLI_SCRIPT /tmp/js-beautify-mkdir/js-beautify-n.js | diff -q /tmp/js-beautify-mkdir/js-beautify-n.js - || { + echo "js-beautify output for /tmp/js-beautify-mkdir/js-beautify-n.js was expected to be unchanged." + exit 1 + } + + $CLI_SCRIPT --eol 'auto' /tmp/js-beautify-mkdir/js-beautify-rn.js | diff -q /tmp/js-beautify-mkdir/js-beautify-rn.js - || { + echo "js-beautify output for /tmp/js-beautify-mkdir/js-beautify-rn.js was expected to be unchanged." + exit 1 + } + # ensure unchanged files are not overwritten + $CLI_SCRIPT -o /tmp/js-beautify-mkdir/js-beautify.js $SCRIPT_DIR/../bin/js-beautify.js cp -p /tmp/js-beautify-mkdir/js-beautify.js /tmp/js-beautify-mkdir/js-beautify-old.js touch /tmp/js-beautify-mkdir/js-beautify.js sleep 2 diff --git a/python/jsbeautifier/__init__.py b/python/jsbeautifier/__init__.py index 25312c6db..41fabbed2 100644 --- a/python/jsbeautifier/__init__.py +++ b/python/jsbeautifier/__init__.py @@ -64,7 +64,7 @@ def __init__(self): self.indent_size = 4 self.indent_char = ' ' self.indent_with_tabs = False - self.eol = '\n' + self.eol = 'auto' self.preserve_newlines = True self.max_preserve_newlines = 10 self.space_in_paren = False @@ -186,7 +186,10 @@ def __init__(self): # Matches a whole line break (where CRLF is considered a single # line break). Used to count lines. + # in javascript, these two differ + # in python they are the same, different methods are called on them self.lineBreak = re.compile(self.six.u("\r\n|[\n\r\u2028\u2029]")) + self.allLineBreaks = self.lineBreak # Test whether a given character code starts an identifier. @@ -259,23 +262,24 @@ def usage(stream=sys.stdout): Input options: - -i, --stdin read input from stdin + -i, --stdin Read input from stdin Output options: - -s, --indent-size=NUMBER indentation size. (default 4). - -c, --indent-char=CHAR character to indent with. (default space). - -e, --eol=STRING character(s) to use as line terminators. (default newline - "\\n") + -s, --indent-size=NUMBER Indentation size. (default 4). + -c, --indent-char=CHAR Character to indent with. (default space). + -e, --eol=STRING Character(s) to use as line terminators. + (default first newline in file, otherwise "\\n") -t, --indent-with-tabs Indent with tabs, overrides -s and -c - -d, --disable-preserve-newlines do not preserve existing line breaks. - -P, --space-in-paren add padding spaces within paren, ie. f( a, b ) + -d, --disable-preserve-newlines Do not preserve existing line breaks. + -P, --space-in-paren Add padding spaces within paren, ie. f( a, b ) -E, --space-in-empty-paren Add a single space inside empty paren, ie. f( ) - -j, --jslint-happy more jslint-compatible output - -a, --space_after_anon_function add a space before an anonymous function's parens, ie. function () - -b, --brace-style=collapse brace style (collapse, expand, end-expand) - -k, --keep-array-indentation keep array indentation. - -r, --replace write output in-place, replacing input - -o, --outfile=FILE specify a file to output to (default stdout) + -j, --jslint-happy More jslint-compatible output + -a, --space_after_anon_function Add a space before an anonymous function's parens, ie. function () + -b, --brace-style=collapse Brace style (collapse, expand, end-expand) + -k, --keep-array-indentation Keep array indentation. + -r, --replace Write output in-place, replacing input + -o, --outfile=FILE Specify a file to output to (default stdout) -f, --keep-function-indentation Do not re-indent function bodies defined in var lines. -x, --unescape-strings Decode printable chars encoded in \\xNN notation. -X, --e4x Pass E4X xml literals through untouched @@ -289,9 +293,9 @@ def usage(stream=sys.stdout): installed. May be useful with some obfuscated script but poses a potential security issue. - -l, --indent-level=NUMBER initial indentation level. (default 0). + -l, --indent-level=NUMBER Initial indentation level. (default 0). - -h, --help, --usage prints this help statement. + -h, --help, --usage Prints this help statement. -v, --version Show the version """, file=stream) @@ -311,8 +315,8 @@ class Beautifier: def __init__(self, opts = default_options() ): self.opts = copy.copy(opts) - self.blank_state() self.acorn = Acorn() + self.blank_state() def blank_state(self, js_source_text = None): @@ -332,6 +336,11 @@ def blank_state(self, js_source_text = None): self.opts.indent_char = "\t" self.opts.indent_size = 1 + if self.opts.eol == 'auto': + self.opts.eol = '\n' + if self.acorn.lineBreak.search(js_source_text or ''): + self.opts.eol = self.acorn.lineBreak.search(js_source_text).group() + self.opts.eol = self.opts.eol.replace('\\r', '\r').replace('\\n', '\n') self.indent_string = self.opts.indent_char * self.opts.indent_size @@ -1141,7 +1150,7 @@ def handle_block_comment(self, current_token): self.output.space_before_token = True return - lines = self.acorn.lineBreak.split(current_token.text) + lines = self.acorn.allLineBreaks.split(current_token.text) javadoc = False starless = False last_indent = current_token.whitespace_before @@ -1601,7 +1610,7 @@ def __tokenize_next(self): comment_match = self.directives_end_ignore_pattern.match(self.input, self.parser_pos) comment += comment_match.group(0) self.parser_pos += len(comment_match.group(0)) - comment = re.sub(self.acorn.lineBreak, '\n', comment) + comment = re.sub(self.acorn.allLineBreaks, '\n', comment) return comment, 'TK_BLOCK_COMMENT', directives if self.input[self.parser_pos] == '/': # peek // comment @@ -1674,7 +1683,7 @@ def __tokenize_next(self): xmlLength = len(xmlStr) self.parser_pos += xmlLength - 1 - xmlStr = re.sub(self.acorn.lineBreak, '\n', xmlStr[:xmlLength]) + xmlStr = re.sub(self.acorn.allLineBreaks, '\n', xmlStr[:xmlLength]) return xmlStr, 'TK_STRING' else: @@ -1726,7 +1735,7 @@ def __tokenize_next(self): while self.parser_pos < len(self.input) and self.acorn.isIdentifierStart(ord(self.input[self.parser_pos])): resulting_string += self.input[self.parser_pos] self.parser_pos += 1 - resulting_string = re.sub(self.acorn.lineBreak, '\n', resulting_string) + resulting_string = re.sub(self.acorn.allLineBreaks, '\n', resulting_string) return resulting_string, 'TK_STRING' @@ -1768,7 +1777,7 @@ def __tokenize_next(self): if template_match: c = template_match.group(0) self.parser_pos += len(c) - 1 - c = re.sub(self.acorn.lineBreak, '\n', c) + c = re.sub(self.acorn.allLineBreaks, '\n', c) return c, 'TK_STRING' @@ -1895,19 +1904,19 @@ def main(): pretty = beautify_file(file, js_options) if outfile == 'stdout': - # python automatically converts newlines in text to windows format - # uncomment this to make it not convert. - # if sys.platform == "win32": - # import msvcrt - # msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) + # python automatically converts newlines in text to "\r\n" when on windows + # switch to binary to prevent this + if sys.platform == "win32": + import msvcrt + msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) sys.stdout.write(pretty) else: if isFileDifferent(outfile, pretty): mkdir_p(os.path.dirname(outfile)) - # python automatically converts newlines in text to windows format - # Change 'w' to 'wb' to make it not convert. - with open(outfile, 'w') as f: + # python automatically converts newlines in text to "\r\n" when on windows + # switch to binary to prevent this + with open(outfile, 'wb') as f: f.write(pretty) except Exception as ex: diff --git a/python/jsbeautifier/tests/shell-smoke-test.sh b/python/jsbeautifier/tests/shell-smoke-test.sh index d00cece8f..e9e498d85 100755 --- a/python/jsbeautifier/tests/shell-smoke-test.sh +++ b/python/jsbeautifier/tests/shell-smoke-test.sh @@ -63,7 +63,7 @@ test_cli_js_beautify() CLI_SCRIPT=$SCRIPT_DIR/../../js-beautify $CLI_SCRIPT $SCRIPT_DIR/../../../js/bin/js-beautify.js > /dev/null || { - echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js was expected succeed." + echo "js-beautify output for $SCRIPT_DIR/../../../js/bin/js-beautify.js was expected succeed." exit 1 } @@ -75,7 +75,6 @@ test_cli_js_beautify() # On windows python automatically converts newlines to windows format # This occurs on both pipes and files. # As a short-term workaround, disabling these two tests on windows. - if [[ "$OSTYPE" != "msys" ]]; then $CLI_SCRIPT $SCRIPT_DIR/../../../js/bin/js-beautify.js | diff $SCRIPT_DIR/../../../js/bin/js-beautify.js - || { $CLI_SCRIPT $SCRIPT_DIR/../../../js/bin/js-beautify.js | diff $SCRIPT_DIR/../../../js/bin/js-beautify.js - | cat -t -e echo "js-beautify output for $SCRIPT_DIR/../../../js/bin/js-beautify.js was expected to be unchanged." @@ -88,7 +87,26 @@ test_cli_js_beautify() 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 } - fi + + # ensure new line settings work + $CLI_SCRIPT -o /tmp/js-beautify-mkdir/js-beautify-n.js -e '\n' $SCRIPT_DIR/../../../js/bin/js-beautify.js + $CLI_SCRIPT -o /tmp/js-beautify-mkdir/js-beautify-rn.js -e '\r\n' /tmp/js-beautify-mkdir/js-beautify-n.js + + diff -q /tmp/js-beautify-mkdir/js-beautify-n.js /tmp/js-beautify-mkdir/js-beautify-rn.js && { + diff /tmp/js-beautify-mkdir/js-beautify-n.js /tmp/js-beautify-mkdir/js-beautify-rn.js | cat -t -e + echo "js-beautify output for /tmp/js-beautify-mkdir/js-beautify-n.js and /tmp/js-beautify-mkdir/js-beautify-rn.js was expected to be different." + exit 1 + } + + $CLI_SCRIPT /tmp/js-beautify-mkdir/js-beautify-n.js | diff -q /tmp/js-beautify-mkdir/js-beautify-n.js - || { + echo "js-beautify output for /tmp/js-beautify-mkdir/js-beautify-n.js was expected to be unchanged." + exit 1 + } + + $CLI_SCRIPT --eol 'auto' /tmp/js-beautify-mkdir/js-beautify-rn.js | diff -q /tmp/js-beautify-mkdir/js-beautify-rn.js - || { + echo "js-beautify output for /tmp/js-beautify-mkdir/js-beautify-rn.js was expected to be unchanged." + exit 1 + } # ensure unchanged files are not overwritten $CLI_SCRIPT -o /tmp/js-beautify-mkdir/js-beautify.js $SCRIPT_DIR/../../../js/bin/js-beautify.js