Skip to content

Commit

Permalink
Merge pull request #1117 from TimvdEijnden/master
Browse files Browse the repository at this point in the history
added preserve_newlines for css
  • Loading branch information
bitwiseman committed Feb 16, 2017
2 parents 183b09b + 4370c5c commit 891f45d
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 6 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Expand Up @@ -31,7 +31,7 @@ Also, check your change needs any tooling updates. For example, the CDN urls re
### 4. Submit a Pull Request

* Run `./build full` locally after commit but before creation of Pull Request. You may start a Pull Request if this does not succeed, but the PR will not be accepted without additional changes.
* Include description of changea. Include examples of input and expected output if possible.
* Include description of changes. Include examples of input and expected output if possible.
* Pull requests must pass build checks on all platforms before being accepted. We use travis-ci and appveyor to run tests on Linux and Windows, across multiple versions of Node.js and Python.

# Folders
Expand Down
28 changes: 27 additions & 1 deletion js/lib/beautify-css.js
Expand Up @@ -41,6 +41,7 @@
The options are (default in brackets):
indent_size (4) — indentation size,
indent_char (space) — character to indent with,
preserve_newlines (default false) - whether existing line breaks should be preserved,
selector_separator_newline (true) - separate selectors with newline or
not (e.g. "a,\nbr" or "a, br")
end_with_newline (false) - end with a newline
Expand Down Expand Up @@ -98,6 +99,7 @@

var indentSize = options.indent_size ? parseInt(options.indent_size, 10) : 4;
var indentCharacter = options.indent_char || ' ';
var preserve_newlines = (options.preserve_newlines === undefined) ? false : options.preserve_newlines;
var selectorSeparatorNewline = (options.selector_separator_newline === undefined) ? true : options.selector_separator_newline;
var end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
var newline_between_rules = (options.newline_between_rules === undefined) ? true : options.newline_between_rules;
Expand Down Expand Up @@ -234,6 +236,16 @@
return false;
}

function removeWhiteSpaceOnEmptyLines(input) {
var output = input.split('\n');
for (var i = 0; i < output.length; i++) {
if (output[i].trim().length === 0) {
output[i] = '';
}
}
return output.join('\n');
}

// printer
var basebaseIndentString = source_text.match(/^[\t ]*/)[0];
var singleIndent = new Array(indentSize + 1).join(indentCharacter);
Expand Down Expand Up @@ -311,6 +323,8 @@
var whitespace = skipWhitespace();
var isAfterSpace = whitespace !== '';
var isAfterNewline = whitespace.indexOf('\n') !== -1;
var newLines = whitespace.replace(/ /g, '').replace(/\t/g, '');
var isAfterEmptyline = newLines.indexOf('\n\n') !== -1;
last_top_ch = top_ch;
top_ch = ch;

Expand Down Expand Up @@ -490,7 +504,15 @@
ch = '=';
output.push(ch);
} else {
print.preserveSingleSpace();
if (isAfterEmptyline && preserve_newlines) {
var newLineCount = newLines.split('\n').length - 2;
for (var i = 0; i < newLineCount; i++) {
print.newLine(true);
}
eatWhitespace();
} else {
print.preserveSingleSpace();
}
output.push(ch);
}
}
Expand All @@ -503,6 +525,10 @@

sweetCode += output.join('').replace(/[\r\n\t ]+$/, '');

if (preserve_newlines) {
sweetCode = removeWhiteSpaceOnEmptyLines(sweetCode);
}

// establish end_with_newline
if (end_with_newline) {
sweetCode += '\n';
Expand Down
5 changes: 3 additions & 2 deletions js/lib/cli.js
Expand Up @@ -360,8 +360,9 @@ function usage(err) {
msg.push(' -E, --extra_liners List of tags (defaults to [head,body,/html] that should have an extra newline');
break;
case "css":
msg.push(' -L, --selector-separator-newline Add a newline between multiple selectors.');
msg.push(' -N, --newline-between-rules Add a newline between CSS rules.');
msg.push(' -L, --selector-separator-newline Add a newline between multiple selectors.');
msg.push(' -N, --newline-between-rules Add a newline between CSS rules.');
msg.push(' -p, --preserve-newlines Preserve line-breaks');
}

if (err) {
Expand Down
22 changes: 22 additions & 0 deletions js/test/generated/beautify-css-tests.js
Expand Up @@ -53,6 +53,7 @@ function run_css_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_bea
default_opts.end_with_newline = false;
default_opts.newline_between_rules = false;
default_opts.space_around_combinator = false;
default_opts.preserve_newlines = false;
default_opts.space_around_selector_separator = false;

function reset_options()
Expand Down Expand Up @@ -324,6 +325,27 @@ function run_css_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_bea
t('a:first-child,a:first-child{color:red;div:first-child,div:hover{color:black;}}', 'a:first-child,\na:first-child {\n\tcolor: red;\n\tdiv:first-child,\n\tdiv:hover {\n\t\tcolor: black;\n\t}\n}');


//============================================================
// Preserve Newlines - (separator_input = "\n\n", separator_output = "\n\n")
reset_options();
opts.preserve_newlines = true;
t('.div {}\n\n.span {}');
t('#bla, #foo{\n\tcolor:black;\n\n\tfont-size: 12px;\n}', '#bla,\n#foo {\n\tcolor: black;\n\n\tfont-size: 12px;\n}');

// Preserve Newlines - (separator_input = "\n\n", separator_output = "\n")
reset_options();
opts.preserve_newlines = false;
t('.div {}\n\n.span {}', '.div {}\n.span {}');
t('#bla, #foo{\n\tcolor:black;\n\n\tfont-size: 12px;\n}', '#bla,\n#foo {\n\tcolor: black;\n\tfont-size: 12px;\n}');


//============================================================
// Preserve Newlines and add tabs
reset_options();
opts.preserve_newlines = true;
t('.tool-tip {\n\tposition: relative;\n\n\t\t\n\t.tool-tip-content {\n\t\t&>* {\n\t\t\tmargin-top: 0;\n\t\t}\n\t\t\n\n\t\t.mixin-box-shadow(.2rem .2rem .5rem rgba(0, 0, 0, .15));\n\t\tpadding: 1rem;\n\t\tposition: absolute;\n\t\tz-index: 10;\n\t}\n}', '.tool-tip {\n\tposition: relative;\n\n\n\t.tool-tip-content {\n\t\t&>* {\n\t\t\tmargin-top: 0;\n\t\t}\n\n\n\t\t.mixin-box-shadow(.2rem .2rem .5rem rgba(0, 0, 0, .15));\n\t\tpadding: 1rem;\n\t\tposition: absolute;\n\t\tz-index: 10;\n\t}\n}');


//============================================================
// Newline Between Rules - (separator = "\n")
reset_options();
Expand Down
25 changes: 23 additions & 2 deletions python/cssbeautifier/__init__.py
Expand Up @@ -35,6 +35,7 @@ def __init__(self):
self.indent_size = 4
self.indent_char = ' '
self.indent_with_tabs = False
self.preserve_newlines = False
self.selector_separator_newline = True
self.end_with_newline = False
self.newline_between_rules = True
Expand Down Expand Up @@ -65,11 +66,12 @@ def __repr__(self):
"""indent_size = %d
indent_char = [%s]
indent_with_tabs = [%s]
preserve_newlines = [%s]
separate_selectors_newline = [%s]
end_with_newline = [%s]
newline_between_rules = [%s]
space_around_combinator = [%s]
""" % (self.indent_size, self.indent_char, self.indent_with_tabs,
""" % (self.indent_size, self.indent_char, self.indent_with_tabs, self.preserve_newlines,
self.selector_separator_newline, self.end_with_newline, self.newline_between_rules,
self.space_around_combinator)

Expand Down Expand Up @@ -329,6 +331,13 @@ def foundNestedPseudoClass(self):

return False

def removeWhiteSpaceOnEmptyLines(self, input):
output = input.split('\n')
for i in range(len(output)):
if len(output[i].strip()) == 0:
output[i] = ''

return '\n'.join(output)

def beautify(self):
m = re.search("^[\t ]*", self.source_text)
Expand All @@ -346,6 +355,8 @@ def beautify(self):
whitespace = self.skipWhitespace()
isAfterSpace = whitespace != ''
isAfterNewline = '\n' in whitespace
newLines = whitespace.replace(" ", "").replace("\t", "")
isAfterEmptyline = '\n\n' in newLines
last_top_ch = top_ch
top_ch = self.ch

Expand Down Expand Up @@ -510,11 +521,21 @@ def beautify(self):
self.ch = '='
printer.push(self.ch)
else:
printer.preserveSingleSpace(isAfterSpace)
if isAfterEmptyline and self.opts.preserve_newlines:
newLineCount = range(len(newLines.split('\n')) - 2)
for i in newLineCount:
printer.newLine(True)

self.eatWhitespace()
else:
printer.preserveSingleSpace(isAfterSpace)
printer.push(self.ch)

sweet_code = re.sub('[\r\n\t ]+$', '', printer.result())

if self.opts.preserve_newlines:
sweet_code = self.removeWhiteSpaceOnEmptyLines(sweet_code)

# establish end_with_newline
if self.opts.end_with_newline:
sweet_code += '\n'
Expand Down
22 changes: 22 additions & 0 deletions python/cssbeautifier/tests/generated/tests.py
Expand Up @@ -58,6 +58,7 @@ def setUpClass(cls):
default_options.end_with_newline = false
default_options.newline_between_rules = false
default_options.space_around_combinator = false
default_options.preserve_newlines = false
default_options.space_around_selector_separator = false

cls.default_options = default_options
Expand Down Expand Up @@ -282,6 +283,27 @@ def testGenerated(self):
t('a:first-child,a:first-child{color:red;div:first-child,div:hover{color:black;}}', 'a:first-child,\na:first-child {\n\tcolor: red;\n\tdiv:first-child,\n\tdiv:hover {\n\t\tcolor: black;\n\t}\n}')


#============================================================
# Preserve Newlines - (separator_input = "\n\n", separator_output = "\n\n")
self.reset_options();
self.options.preserve_newlines = true
t('.div {}\n\n.span {}')
t('#bla, #foo{\n\tcolor:black;\n\n\tfont-size: 12px;\n}', '#bla,\n#foo {\n\tcolor: black;\n\n\tfont-size: 12px;\n}')

# Preserve Newlines - (separator_input = "\n\n", separator_output = "\n")
self.reset_options();
self.options.preserve_newlines = false
t('.div {}\n\n.span {}', '.div {}\n.span {}')
t('#bla, #foo{\n\tcolor:black;\n\n\tfont-size: 12px;\n}', '#bla,\n#foo {\n\tcolor: black;\n\tfont-size: 12px;\n}')


#============================================================
# Preserve Newlines and add tabs
self.reset_options();
self.options.preserve_newlines = true
t('.tool-tip {\n\tposition: relative;\n\n\t\t\n\t.tool-tip-content {\n\t\t&>* {\n\t\t\tmargin-top: 0;\n\t\t}\n\t\t\n\n\t\t.mixin-box-shadow(.2rem .2rem .5rem rgba(0, 0, 0, .15));\n\t\tpadding: 1rem;\n\t\tposition: absolute;\n\t\tz-index: 10;\n\t}\n}', '.tool-tip {\n\tposition: relative;\n\n\n\t.tool-tip-content {\n\t\t&>* {\n\t\t\tmargin-top: 0;\n\t\t}\n\n\n\t\t.mixin-box-shadow(.2rem .2rem .5rem rgba(0, 0, 0, .15));\n\t\tpadding: 1rem;\n\t\tposition: absolute;\n\t\tz-index: 10;\n\t}\n}')


#============================================================
# Newline Between Rules - (separator = "\n")
self.reset_options();
Expand Down
29 changes: 29 additions & 0 deletions test/data/css/tests.js
Expand Up @@ -32,6 +32,7 @@ exports.test_data = {
{ name: "end_with_newline", value: "false" },
{ name: "newline_between_rules", value: "false" },
{ name: "space_around_combinator", value: "false" },
{ name: "preserve_newlines", value: "false" },
// deprecated
{ name: "space_around_selector_separator", value: "false" }
],
Expand Down Expand Up @@ -208,6 +209,34 @@ exports.test_data = {
output: 'a:first-child,{{separator}}a:first-child {\n\tcolor: red;\n\tdiv:first-child,{{separator1}}div:hover {\n\t\tcolor: black;\n\t}\n}'
}
]
}, {
name: "Preserve Newlines",
description: "",
matrix: [{
options: [
{ name: "preserve_newlines", value: "true" }
],
separator_input: '\\n\\n',
separator_output: '\\n\\n',
}, {
options: [
{ name: "preserve_newlines", value: "false" }
],
separator_input: '\\n\\n',
separator_output: '\\n',
}],
tests: [
{ input: '.div {}{{separator_input}}.span {}', output: '.div {}{{separator_output}}.span {}' },
{ input: '#bla, #foo{\n\tcolor:black;{{separator_input}}\tfont-size: 12px;\n}', output: '#bla,\n#foo {\n\tcolor: black;{{separator_output}}\tfont-size: 12px;\n}' }
],
}, {
name: "Preserve Newlines and add tabs",
options: [{ name: "preserve_newlines", value: "true" }],
description: "",
tests: [{
input: '.tool-tip {\n\tposition: relative;\n\n\t\t\n\t.tool-tip-content {\n\t\t&>* {\n\t\t\tmargin-top: 0;\n\t\t}\n\t\t\n\n\t\t.mixin-box-shadow(.2rem .2rem .5rem rgba(0, 0, 0, .15));\n\t\tpadding: 1rem;\n\t\tposition: absolute;\n\t\tz-index: 10;\n\t}\n}',
output: '.tool-tip {\n\tposition: relative;\n\n\n\t.tool-tip-content {\n\t\t&>* {\n\t\t\tmargin-top: 0;\n\t\t}\n\\n\\n\t\t.mixin-box-shadow(.2rem .2rem .5rem rgba(0, 0, 0, .15));\n\t\tpadding: 1rem;\n\t\tposition: absolute;\n\t\tz-index: 10;\n\t}\n}'
}],
}, {
name: "Newline Between Rules",
description: "",
Expand Down

0 comments on commit 891f45d

Please sign in to comment.