Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add expand brace-style option to css beautifier #1796

Merged
merged 1 commit into from
Jul 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ CSS Beautifier Options:
-t, --indent-with-tabs Indent with tabs, overrides -s and -c
-e, --eol Character(s) to use as line terminators. (default newline - "\\n")
-n, --end-with-newline End output with newline
-b, --brace-style [collapse|expand] ["collapse"]
-L, --selector-separator-newline Add a newline between multiple selectors
-N, --newline-between-rules Add a newline between CSS rules
--indent-empty-lines Keep indentation on empty lines
Expand Down
1 change: 1 addition & 0 deletions js/src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ function usage(err) {
msg.push(' --unformatted_content_delimiter Keep text content together between this string [""]');
break;
case "css":
msg.push(' -b, --brace-style [collapse|expand] ["collapse"]');
msg.push(' -L, --selector-separator-newline Add a newline between multiple selectors.');
msg.push(' -N, --newline-between-rules Add a newline between CSS rules.');
}
Expand Down
21 changes: 16 additions & 5 deletions js/src/css/beautifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -291,23 +291,34 @@ Beautifier.prototype.beautify = function() {
insidePropertyValue = false;
this.outdent();
}
this.indent();
this._output.space_before_token = true;
this.print_string(this._ch);

// when entering conditional groups, only rulesets are allowed
if (enteringConditionalGroup) {
enteringConditionalGroup = false;
insideRule = (this._indentLevel > this._nestedLevel);
insideRule = (this._indentLevel >= this._nestedLevel);
} else {
// otherwise, declarations are also allowed
insideRule = (this._indentLevel >= this._nestedLevel);
insideRule = (this._indentLevel >= this._nestedLevel - 1);
}
if (this._options.newline_between_rules && insideRule) {
if (this._output.previous_line && this._output.previous_line.item(-1) !== '{') {
this._output.ensure_empty_line_above('/', ',');
}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved this section below to prevent ( when using newline_between_rules )
.a{} .b{} -->

.a
/* there shouldn't be an empty line here */
{}

.b
/* there shouldn't be an empty line here */
{}

On the other hand the branching is to indent the { correctly

this._output.space_before_token = true;

// The difference in print_string and indent order is necessary to indent the '{' correctly
if (this._options.brace_style === 'expand') {
this._output.add_new_line();
this.print_string(this._ch);
this.indent();
this._output.set_indent(this._indentLevel);
} else {
this.indent();
this.print_string(this._ch);
}

this.eatWhitespace(true);
this._output.add_new_line();
} else if (this._ch === '}') {
Expand Down
10 changes: 10 additions & 0 deletions js/src/css/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ function Options(options) {
var space_around_selector_separator = this._get_boolean('space_around_selector_separator');
this.space_around_combinator = this._get_boolean('space_around_combinator') || space_around_selector_separator;

var brace_style_split = this._get_selection_list('brace_style', ['collapse', 'expand', 'end-expand', 'none', 'preserve-inline']);
this.brace_style = 'collapse';
for (var bs = 0; bs < brace_style_split.length; bs++) {
if (brace_style_split[bs] !== 'expand') {
// default to collapse, as only collapse|expand is implemented for now
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect.

this.brace_style = 'collapse';
} else {
this.brace_style = brace_style_split[bs];
}
}
}
Options.prototype = new BaseOptions();

Expand Down
202 changes: 202 additions & 0 deletions js/test/generated/beautify-css-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ function run_css_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_bea
default_opts.indent_size = 4;
default_opts.indent_char = ' ';
default_opts.selector_separator_newline = true;
default_opts.brace_style = 'collapse';
default_opts.end_with_newline = false;
default_opts.newline_between_rules = false;
default_opts.space_around_combinator = false;
Expand Down Expand Up @@ -992,6 +993,78 @@ function run_css_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_bea
' }\n' +
'}');

// Selector Separator - (selector_separator_newline = "true", selector_separator = "" "", brace_style = ""expand"", newline_between_rules = "false")
reset_options();
set_name('Selector Separator - (selector_separator_newline = "true", selector_separator = "" "", brace_style = ""expand"", newline_between_rules = "false")');
opts.selector_separator_newline = true;
opts.selector_separator = " ";
opts.brace_style = "expand";
opts.newline_between_rules = false;
t(
'#bla, #foo{color:green}',
// -- output --
'#bla,\n#foo\n{\n' +
' color: green\n' +
'}');
t(
'#bla, #foo{color:green}\n' +
'#bla, #foo{color:green}',
// -- output --
'#bla,\n#foo\n{\n' +
' color: green\n' +
'}\n' +
'#bla,\n#foo\n{\n' +
' color: green\n' +
'}');
t(
'@media print {.tab{}}',
// -- output --
'@media print\n{\n' +
' .tab\n {}\n' +
'}');

// This is bug #1489
t(
'@media print {.tab,.bat{}}',
// -- output --
'@media print\n{\n' +
' .tab,\n .bat\n {}\n' +
'}');

// This is bug #1489
t(
'@media print {// comment\n' +
'//comment 2\n' +
'.bat{}}',
// -- output --
'@media print\n{\n' +
' // comment\n' +
' //comment 2\n' +
' .bat\n {}\n' +
'}');
t(
'#bla, #foo{color:black}',
// -- output --
'#bla,\n#foo\n{\n' +
' color: black\n' +
'}');
t(
'a:first-child,a:first-child{color:red;div:first-child,div:hover{color:black;}}\n' +
'a:first-child,a:first-child{color:red;div:first-child,div:hover{color:black;}}',
// -- output --
'a:first-child,\na:first-child\n{\n' +
' color: red;\n' +
' div:first-child,\n div:hover\n {\n' +
' color: black;\n' +
' }\n' +
'}\n' +
'a:first-child,\na:first-child\n{\n' +
' color: red;\n' +
' div:first-child,\n div:hover\n {\n' +
' color: black;\n' +
' }\n' +
'}');


//============================================================
// Preserve Newlines - (preserve_newlines = "true")
Expand Down Expand Up @@ -10784,6 +10857,135 @@ function run_css_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_bea
'}');


//============================================================
// brace_style = expand - (brace_style = ""expand"", selector_separator_newline = "false", newline_between_rules = "true")
reset_options();
set_name('brace_style = expand - (brace_style = ""expand"", selector_separator_newline = "false", newline_between_rules = "true")');
opts.brace_style = 'expand';
opts.selector_separator_newline = false;
opts.newline_between_rules = true;
t(
'a, b, .c {\n' +
' width: auto;\n' +
' \n' +
' height: auto;\n' +
'}',
// -- output --
'a, b, .c\n' +
'{\n' +
' width: auto;\n' +
' height: auto;\n' +
'}');

// edge case - empty line after { should not be indented without indent_empty_lines
t(
'a, b, .c {\n' +
'\n' +
' width: auto;\n' +
'}',
// -- output --
'a, b, .c\n' +
'{\n' +
' width: auto;\n' +
'}');

// integration test of newline_between_rules, imports, and brace_style="expand"
t(
'.a{} @import "custom.css";.rule{}',
// -- output --
'.a\n' +
'{}\n' +
'\n' +
'@import "custom.css";\n' +
'\n' +
'.rule\n' +
'{}');

// brace_style = expand - (brace_style = ""expand"", indent_empty_lines = "true", selector_separator_newline = "false", preserve_newlines = "true")
reset_options();
set_name('brace_style = expand - (brace_style = ""expand"", indent_empty_lines = "true", selector_separator_newline = "false", preserve_newlines = "true")');
opts.brace_style = 'expand';
opts.indent_empty_lines = true;
opts.selector_separator_newline = false;
opts.preserve_newlines = true;
t(
'a, b, .c {\n' +
' width: auto;\n' +
' \n' +
' height: auto;\n' +
'}',
// -- output --
'a, b, .c\n' +
'{\n' +
' width: auto;\n' +
' \n height: auto;\n' +
'}');

// edge case - empty line after { should not be indented without indent_empty_lines
t(
'a, b, .c {\n' +
'\n' +
' width: auto;\n' +
'}',
// -- output --
'a, b, .c\n' +
'{\n' +
' \n width: auto;\n' +
'}');

// integration test of newline_between_rules, imports, and brace_style="expand"
t(
'.a{} @import "custom.css";.rule{}',
// -- output --
'.a\n' +
'{}\n' +
'@import "custom.css";\n' +
'.rule\n' +
'{}');

// brace_style = expand - (brace_style = ""expand"", indent_empty_lines = "false", selector_separator_newline = "false", preserve_newlines = "true")
reset_options();
set_name('brace_style = expand - (brace_style = ""expand"", indent_empty_lines = "false", selector_separator_newline = "false", preserve_newlines = "true")');
opts.brace_style = 'expand';
opts.indent_empty_lines = false;
opts.selector_separator_newline = false;
opts.preserve_newlines = true;
t(
'a, b, .c {\n' +
' width: auto;\n' +
' \n' +
' height: auto;\n' +
'}',
// -- output --
'a, b, .c\n' +
'{\n' +
' width: auto;\n' +
'\n height: auto;\n' +
'}');

// edge case - empty line after { should not be indented without indent_empty_lines
t(
'a, b, .c {\n' +
'\n' +
' width: auto;\n' +
'}',
// -- output --
'a, b, .c\n' +
'{\n' +
'\n width: auto;\n' +
'}');

// integration test of newline_between_rules, imports, and brace_style="expand"
t(
'.a{} @import "custom.css";.rule{}',
// -- output --
'.a\n' +
'{}\n' +
'@import "custom.css";\n' +
'.rule\n' +
'{}');


//============================================================
// LESS mixins
reset_options();
Expand Down
10 changes: 7 additions & 3 deletions python/cssbeautifier/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def usage(stream=sys.stdout):
--preserve-newlines Preserve existing line breaks.
--disable-selector-separator-newline
Do not print each selector on a separate line.
-b, --brace-style=collapse Brace style (collapse, expand)
-n, --end-with-newline End output with newline
--disable-newline-between-rules
Do not print empty line between rules.
Expand All @@ -113,11 +114,12 @@ def main():
argv = sys.argv[1:]

try:
opts, args = getopt.getopt(argv, "hvio:rs:c:e:tn",
opts, args = getopt.getopt(argv, "hvio:rs:c:e:tnb:",
['help', 'usage', 'version', 'stdin', 'outfile=', 'replace',
'indent-size=', 'indent-char=', 'eol=', 'indent-with-tabs',
'preserve-newlines', 'disable-selector-separator-newline',
'end-with-newline', 'disable-newline-between-rules',
'preserve-newlines', 'brace-style=',
'disable-selector-separator-newline', 'end-with-newline',
'disable-newline-between-rules',
'space-around-combinator', 'indent-empty-lines'])
except getopt.GetoptError as ex:
print(ex, file=sys.stderr)
Expand Down Expand Up @@ -155,6 +157,8 @@ def main():
css_options.preserve_newlines = True
elif opt in ('--disable-selector-separator-newline'):
css_options.selector_separator_newline = False
elif opt in ('--brace-style', '-b'):
css_options.brace_style = arg
elif opt in ('--end-with-newline', '-n'):
css_options.end_with_newline = True
elif opt in ('--disable-newline-between-rules'):
Expand Down
21 changes: 16 additions & 5 deletions python/cssbeautifier/css/beautifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,24 +314,35 @@ def beautify(self):
if insidePropertyValue:
insidePropertyValue = False
self.outdent()
self.indent()
self._output.space_before_token = True
self.print_string(self._ch)

# when entering conditional groups, only rulesets are
# allowed
if enteringConditionalGroup:
enteringConditionalGroup = False
insideRule = self._indentLevel > self._nestedLevel
insideRule = self._indentLevel >= self._nestedLevel
else:
# otherwise, declarations are also allowed
insideRule = self._indentLevel >= self._nestedLevel
insideRule = self._indentLevel >= self._nestedLevel - 1

if self._options.newline_between_rules and insideRule:
if self._output.previous_line and \
not self._output.previous_line.is_empty() and \
self._output.previous_line.item(-1) != '{':
self._output.ensure_empty_line_above('/', ',')

self._output.space_before_token = True

# The difference in print_string and indent order
# is necessary to indent the '{' correctly
if self._options.brace_style == 'expand':
self._output.add_new_line()
self.print_string(self._ch)
self.indent()
self._output.set_indent(self._indentLevel)
else:
self.indent()
self.print_string(self._ch)

self.eatWhitespace(True)
self._output.add_new_line()
elif self._ch == '}':
Expand Down
9 changes: 9 additions & 0 deletions python/cssbeautifier/css/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ def __init__(self, options=None):
self.selector_separator_newline = self._get_boolean('selector_separator_newline', True)
self.newline_between_rules = self._get_boolean('newline_between_rules', True)

brace_style_split = self._get_selection_list('brace_style', ['collapse', 'expand', 'end-expand', 'none', 'preserve-inline'])
self.brace_style = 'collapse'
for bs in brace_style_split:
if bs != 'expand':
# default to collapse, as only collapse|expand is implemented for now
self.brace_style = 'collapse'
else:
self.brace_style = bs

# deprecated
space_around_selector_separator = self._get_boolean('space_around_selector_separator')

Expand Down
Loading