Skip to content

Commit

Permalink
Add "eol" option to support non-newline line endings
Browse files Browse the repository at this point in the history
Note: this is still a bit hacky.  There are cases in some constructs where newlines will not be normalized.
We'll address these as people see the need for them.  This handles the 90% case.

Fixes #260
  • Loading branch information
bitwiseman committed Mar 23, 2015
1 parent 8c3ddc5 commit 59e9d04
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 3 deletions.
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -86,6 +86,7 @@ CLI Options:
Beautifier Options:
-s, --indent-size Indentation size [4]
-c, --indent-char Indentation character [" "]
-e, --eol character(s) to use as line terminators. (default newline - "\\n")');
-l, --indent-level Initial indentation level [0]
-t, --indent-with-tabs Indent with tabs, overrides -s and -c
-p, --preserve-newlines Preserve line-breaks (--no-preserve-newlines disables)
Expand All @@ -110,6 +111,7 @@ These largely correspond to the underscored option keys for both library interfa
{
"indent_size": 4,
"indent_char": " ",
"eol": "\n",
"indent_level": 0,
"indent_with_tabs": false,
"preserve_newlines": true,
Expand Down
12 changes: 12 additions & 0 deletions js/lib/beautify.js
Expand Up @@ -265,6 +265,7 @@

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.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);
Expand All @@ -291,6 +292,8 @@
opt.indent_size = 1;
}

opt.eol = opt.eol.replace(/\\r/, '\r').replace(/\\n/, '\n')

//----------------------------------
indent_string = '';
while (opt.indent_size > 0) {
Expand Down Expand Up @@ -355,6 +358,10 @@
sweet_code += '\n';
}

if (opt.eol != '\n') {
sweet_code = sweet_code.replace(/[\r]?[\n]/mg, opt.eol);
}

return sweet_code;
};

Expand Down Expand Up @@ -1788,6 +1795,11 @@
(esc || (input.charAt(parser_pos) !== sep &&
(sep === '`' || !acorn.newline.test(input.charAt(parser_pos)))))) {
resulting_string += input.charAt(parser_pos);
// Handle \r\n linebreaks after escapes or in template strings
if (input.charAt(parser_pos) === '\r' && input.charAt(parser_pos + 1) === '\n') {
parser_pos += 1;
resulting_string += '\n';
}
if (esc) {
if (input.charAt(parser_pos) === 'x' || input.charAt(parser_pos) === 'u') {
has_char_escapes = true;
Expand Down
3 changes: 3 additions & 0 deletions js/lib/cli.js
Expand Up @@ -46,6 +46,7 @@ var fs = require('fs'),
// Beautifier
"indent_size": Number,
"indent_char": String,
"eol": String,
"indent_level": Number,
"indent_with_tabs": Boolean,
"preserve_newlines": Boolean,
Expand Down Expand Up @@ -89,6 +90,7 @@ var fs = require('fs'),
// Beautifier
"s": ["--indent_size"],
"c": ["--indent_char"],
"e": ["--eol"],
"l": ["--indent_level"],
"t": ["--indent_with_tabs"],
"p": ["--preserve_newlines"],
Expand Down Expand Up @@ -223,6 +225,7 @@ function usage(err) {
case "js":
msg.push(' -l, --indent-level Initial indentation level [0]');
msg.push(' -t, --indent-with-tabs Indent with tabs, overrides -s and -c');
msg.push(' -e, --eol character(s) to use as line terminators. (default newline - "\\n")');
msg.push(' -p, --preserve-newlines Preserve line-breaks (--no-preserve-newlines disables)');
msg.push(' -m, --max-preserve-newlines Number of line-breaks to be preserved in one chunk [10]');
msg.push(' -P, --space-in-paren Add padding spaces within paren, ie. f( a, b )');
Expand Down
7 changes: 7 additions & 0 deletions js/test/beautify-javascript-tests.js
Expand Up @@ -65,6 +65,13 @@ function run_javascript_tests(test_obj, Urlencoded, js_beautify, html_beautify,
wrapped_input = '{\n' + input.replace(/^(.+)$/mg, ' $1') + '\n foo = bar;\n}';
wrapped_expectation = '{\n' + expectation.replace(/^(.+)$/mg, ' $1') + '\n foo = bar;\n}';
test_fragment(wrapped_input, wrapped_expectation);

// Everywhere we do newlines, they should be replaced with opts.eol
opts.eol = '\r\\n';
wrapped_input = wrapped_input.replace(/[\n]/mg, '\r\n');
wrapped_expectation = wrapped_expectation.replace(/[\n]/mg, '\r\n');
test_fragment(wrapped_input, wrapped_expectation);
opts.eol = '\n';
}

}
Expand Down
21 changes: 18 additions & 3 deletions python/jsbeautifier/__init__.py
Expand Up @@ -64,6 +64,7 @@ def __init__(self):
self.indent_size = 4
self.indent_char = ' '
self.indent_with_tabs = False
self.eol = '\n'
self.preserve_newlines = True
self.max_preserve_newlines = 10
self.space_in_paren = False
Expand Down Expand Up @@ -260,6 +261,7 @@ def usage(stream=sys.stdout):
-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")
-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 )
Expand Down Expand Up @@ -325,6 +327,8 @@ def blank_state(self, js_source_text = None):
self.opts.indent_char = "\t"
self.opts.indent_size = 1

self.opts.eol = self.opts.eol.replace('\\r', '\r').replace('\\n', '\n')

self.indent_string = self.opts.indent_char * self.opts.indent_size

self.baseIndentString = ''
Expand Down Expand Up @@ -398,7 +402,11 @@ def beautify(self, s, opts = None ):

sweet_code = self.output.get_code()
if self.opts.end_with_newline:
sweet_code += "\n"
sweet_code += self.opts.eol

if not self.opts.eol == '\n':
sweet_code = sweet_code.replace('\r\n', '\n')
sweet_code = sweet_code.replace('\n', self.opts.eol)

return sweet_code

Expand Down Expand Up @@ -1594,6 +1602,11 @@ def __tokenize_next(self):
(esc or (self.input[self.parser_pos] != sep and
(sep == '`' or not self.acorn.newline.match(self.input[self.parser_pos])))):
resulting_string += self.input[self.parser_pos]
# Handle \r\n linebreaks after escapes or in template strings
if self.input[self.parser_pos] == '\r' and self.parser_pos + 1 < len(self.input) and self.input[self.parser_pos + 1] == '\n':
self.parser_pos += 1
resulting_string += '\n'

if esc1 and esc1 >= esc2:
try:
esc1 = int(resulting_string[-esc2:], 16)
Expand Down Expand Up @@ -1713,8 +1726,8 @@ def main():
argv = sys.argv[1:]

try:
opts, args = getopt.getopt(argv, "s:c:o:rdEPjabkil:xhtfvXnCw:",
['indent-size=','indent-char=','outfile=', 'replace', 'disable-preserve-newlines',
opts, args = getopt.getopt(argv, "s:c:e:o:rdEPjabkil:xhtfvXnCw:",
['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',
Expand Down Expand Up @@ -1744,6 +1757,8 @@ def main():
js_options.indent_size = int(arg)
elif opt in ('--indent-char', '-c'):
js_options.indent_char = arg
elif opt in ('--eol', '-e'):
js_options.eol = arg
elif opt in ('--indent-with-tabs', '-t'):
js_options.indent_with_tabs = True
elif opt in ('--disable-preserve-newlines', '-d'):
Expand Down
10 changes: 10 additions & 0 deletions python/jsbeautifier/tests/testjsbeautifier.py
Expand Up @@ -2263,6 +2263,14 @@ def bt(self, input, expectation=None):
wrapped_expect = '{\n%s\n foo = bar;\n}' % self.wrap(expectation)
self.decodesto(wrapped_input, wrapped_expect)

# Everywhere we do newlines, they should be replaced with opts.eol
self.options.eol = '\r\\n';
wrapped_input = wrapped_input.replace('\n', '\r\n')
wrapped_expect = wrapped_expect.replace('\n', '\r\n')
self.decodesto(wrapped_input, wrapped_expect)
self.options.eol = '\n'


@classmethod
def setUpClass(cls):
options = jsbeautifier.default_options()
Expand All @@ -2274,6 +2282,8 @@ def setUpClass(cls):
options.brace_style = 'collapse'
options.indent_level = 0
options.break_chained_methods = False
options.eol = '\n'


cls.options = options
cls.wrapregex = re.compile('^(.+)$', re.MULTILINE)
Expand Down
7 changes: 7 additions & 0 deletions test/template/node-javascript.mustache
Expand Up @@ -65,6 +65,13 @@ function run_javascript_tests(test_obj, Urlencoded, js_beautify, html_beautify,
wrapped_input = '{\n' + input.replace(/^(.+)$/mg, ' $1') + '\n foo = bar;\n}';
wrapped_expectation = '{\n' + expectation.replace(/^(.+)$/mg, ' $1') + '\n foo = bar;\n}';
test_fragment(wrapped_input, wrapped_expectation);
// Everywhere we do newlines, they should be replaced with opts.eol
opts.eol = '\r\\n';
wrapped_input = wrapped_input.replace(/[\n]/mg, '\r\n');
wrapped_expectation = wrapped_expectation.replace(/[\n]/mg, '\r\n');
test_fragment(wrapped_input, wrapped_expectation);
opts.eol = '\n';
}

}
Expand Down
10 changes: 10 additions & 0 deletions test/template/python-javascript.mustache
Expand Up @@ -1227,6 +1227,14 @@ class TestJSBeautifier(unittest.TestCase):
wrapped_expect = '{\n%s\n foo = bar;\n}' % self.wrap(expectation)
self.decodesto(wrapped_input, wrapped_expect)

# Everywhere we do newlines, they should be replaced with opts.eol
self.options.eol = '\r\\n';
wrapped_input = wrapped_input.replace('\n', '\r\n')
wrapped_expect = wrapped_expect.replace('\n', '\r\n')
self.decodesto(wrapped_input, wrapped_expect)
self.options.eol = '\n'


@classmethod
def setUpClass(cls):
options = jsbeautifier.default_options()
Expand All @@ -1238,6 +1246,8 @@ class TestJSBeautifier(unittest.TestCase):
options.brace_style = 'collapse'
options.indent_level = 0
options.break_chained_methods = False
options.eol = '\n'


cls.options = options
cls.wrapregex = re.compile('^(.+)$', re.MULTILINE)
Expand Down

0 comments on commit 59e9d04

Please sign in to comment.