mirror of
https://github.com/beautifier/js-beautify.git
synced 2025-02-17 03:38:08 +00:00
Add minimum attributes option for html attribute wrap
This commit is contained in:
parent
8603142b1e
commit
6edbfb5450
@ -340,6 +340,7 @@ HTML Beautifier Options:
|
||||
-S, --indent-scripts [keep|separate|normal] ["normal"]
|
||||
-w, --wrap-line-length Maximum characters per line (0 disables) [250]
|
||||
-A, --wrap-attributes Wrap attributes to new lines [auto|force|force-aligned|force-expand-multiline|aligned-multiple|preserve|preserve-aligned] ["auto"]
|
||||
-M, --wrap-attributes-min-attrs Minimum number of html tag attributes for force wrapping attributes
|
||||
-i, --wrap-attributes-indent-size Indent wrapped attributes to after N characters [indent-size] (ignored if wrap-attributes is "aligned")
|
||||
-d, --inline List of tags to be considered inline tags
|
||||
-U, --unformatted List of tags (defaults to inline) that should not be reformatted
|
||||
|
@ -87,6 +87,7 @@ var path = require('path'),
|
||||
"unescape_strings": Boolean,
|
||||
"wrap_line_length": Number,
|
||||
"wrap_attributes": ["auto", "force", "force-aligned", "force-expand-multiline", "aligned-multiple", "preserve", "preserve-aligned"],
|
||||
"wrap_attributes_min_attrs": Number,
|
||||
"wrap_attributes_indent_size": Number,
|
||||
"e4x": Boolean,
|
||||
"end_with_newline": Boolean,
|
||||
@ -151,6 +152,7 @@ var path = require('path'),
|
||||
"N": ["--newline_between_rules"],
|
||||
// HTML-only
|
||||
"A": ["--wrap_attributes"],
|
||||
"M": ["--wrap_attributes_min_attrs"],
|
||||
"i": ["--wrap_attributes_indent_size"],
|
||||
"W": ["--max_char"], // obsolete since 1.3.5
|
||||
"d": ["--inline"],
|
||||
@ -383,6 +385,7 @@ function usage(err) {
|
||||
msg.push(' -S, --indent-scripts [keep|separate|normal] ["normal"]');
|
||||
msg.push(' -w, --wrap-line-length Wrap lines that exceed N characters [0]');
|
||||
msg.push(' -A, --wrap-attributes Wrap html tag attributes to new lines [auto|force|force-aligned|force-expand-multiline|aligned-multiple|preserve|preserve-aligned] ["auto"]');
|
||||
msg.push(' -M, --wrap-attributes-min-attrs Minimum number of html tag attributes for force wrapping attributes [2]');
|
||||
msg.push(' -i, --wrap-attributes-indent-size Indent wrapped tags to after N characters [indent-level]');
|
||||
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]');
|
||||
|
@ -296,11 +296,11 @@ Beautifier.prototype.beautify = function() {
|
||||
while (raw_token.type !== TOKEN.EOF) {
|
||||
|
||||
if (raw_token.type === TOKEN.TAG_OPEN || raw_token.type === TOKEN.COMMENT) {
|
||||
parser_token = this._handle_tag_open(printer, raw_token, last_tag_token, last_token);
|
||||
parser_token = this._handle_tag_open(printer, raw_token, last_tag_token, last_token, tokens);
|
||||
last_tag_token = parser_token;
|
||||
} else if ((raw_token.type === TOKEN.ATTRIBUTE || raw_token.type === TOKEN.EQUALS || raw_token.type === TOKEN.VALUE) ||
|
||||
(raw_token.type === TOKEN.TEXT && !last_tag_token.tag_complete)) {
|
||||
parser_token = this._handle_inside_tag(printer, raw_token, last_tag_token, tokens);
|
||||
parser_token = this._handle_inside_tag(printer, raw_token, last_tag_token, last_token);
|
||||
} else if (raw_token.type === TOKEN.TAG_CLOSE) {
|
||||
parser_token = this._handle_tag_close(printer, raw_token, last_tag_token);
|
||||
} else if (raw_token.type === TOKEN.TEXT) {
|
||||
@ -357,7 +357,7 @@ Beautifier.prototype._handle_tag_close = function(printer, raw_token, last_tag_t
|
||||
return parser_token;
|
||||
};
|
||||
|
||||
Beautifier.prototype._handle_inside_tag = function(printer, raw_token, last_tag_token, tokens) {
|
||||
Beautifier.prototype._handle_inside_tag = function(printer, raw_token, last_tag_token, last_token) {
|
||||
var wrapped = last_tag_token.has_wrapped_attrs;
|
||||
var parser_token = {
|
||||
text: raw_token.text,
|
||||
@ -378,7 +378,6 @@ Beautifier.prototype._handle_inside_tag = function(printer, raw_token, last_tag_
|
||||
} else {
|
||||
if (raw_token.type === TOKEN.ATTRIBUTE) {
|
||||
printer.set_space_before_token(true);
|
||||
last_tag_token.attr_count += 1;
|
||||
} else if (raw_token.type === TOKEN.EQUALS) { //no space before =
|
||||
printer.set_space_before_token(false);
|
||||
} else if (raw_token.type === TOKEN.VALUE && raw_token.previous.type === TOKEN.EQUALS) { //no space before value
|
||||
@ -391,29 +390,15 @@ Beautifier.prototype._handle_inside_tag = function(printer, raw_token, last_tag_
|
||||
wrapped = wrapped || raw_token.newlines !== 0;
|
||||
}
|
||||
|
||||
|
||||
if (this._is_wrap_attributes_force) {
|
||||
var force_attr_wrap = last_tag_token.attr_count > 1;
|
||||
if (this._is_wrap_attributes_force_expand_multiline && last_tag_token.attr_count === 1) {
|
||||
var is_only_attribute = true;
|
||||
var peek_index = 0;
|
||||
var peek_token;
|
||||
do {
|
||||
peek_token = tokens.peek(peek_index);
|
||||
if (peek_token.type === TOKEN.ATTRIBUTE) {
|
||||
is_only_attribute = false;
|
||||
break;
|
||||
}
|
||||
peek_index += 1;
|
||||
} while (peek_index < 4 && peek_token.type !== TOKEN.EOF && peek_token.type !== TOKEN.TAG_CLOSE);
|
||||
|
||||
force_attr_wrap = !is_only_attribute;
|
||||
}
|
||||
|
||||
if (force_attr_wrap) {
|
||||
printer.print_newline(false);
|
||||
wrapped = true;
|
||||
}
|
||||
// Wrap for 'force' options, and if the number of attributes is at least that specified in 'wrap_attributes_min_attrs':
|
||||
// 1. always wrap the second and beyond attributes
|
||||
// 2. wrap the first attribute only if 'force-expand-multiline' is used and there are more than 1 attributes
|
||||
if (this._is_wrap_attributes_force &&
|
||||
last_tag_token.attr_count >= this._options.wrap_attributes_min_attrs &&
|
||||
(last_token.type !== TOKEN.TAG_OPEN || // ie. second attribute and beyond
|
||||
(this._is_wrap_attributes_force_expand_multiline && last_tag_token.attr_count > 1))) {
|
||||
printer.print_newline(false);
|
||||
wrapped = true;
|
||||
}
|
||||
}
|
||||
printer.print_token(raw_token);
|
||||
@ -542,12 +527,12 @@ Beautifier.prototype._print_custom_beatifier_text = function(printer, raw_token,
|
||||
}
|
||||
};
|
||||
|
||||
Beautifier.prototype._handle_tag_open = function(printer, raw_token, last_tag_token, last_token) {
|
||||
Beautifier.prototype._handle_tag_open = function(printer, raw_token, last_tag_token, last_token, tokens) {
|
||||
var parser_token = this._get_tag_open_token(raw_token);
|
||||
|
||||
if ((last_tag_token.is_unformatted || last_tag_token.is_content_unformatted) &&
|
||||
!last_tag_token.is_empty_element &&
|
||||
raw_token.type === TOKEN.TAG_OPEN && raw_token.text.indexOf('</') === 0) {
|
||||
raw_token.type === TOKEN.TAG_OPEN && !parser_token.is_start_tag) {
|
||||
// End element tags for unformatted or content_unformatted elements
|
||||
// are printed raw to keep any newlines inside them exactly the same.
|
||||
printer.add_raw_token(raw_token);
|
||||
@ -561,6 +546,19 @@ Beautifier.prototype._handle_tag_open = function(printer, raw_token, last_tag_to
|
||||
printer.print_token(raw_token);
|
||||
}
|
||||
|
||||
// count the number of attributes
|
||||
if (parser_token.is_start_tag && this._is_wrap_attributes_force) {
|
||||
var peek_index = 0;
|
||||
var peek_token;
|
||||
do {
|
||||
peek_token = tokens.peek(peek_index);
|
||||
if (peek_token.type === TOKEN.ATTRIBUTE) {
|
||||
parser_token.attr_count += 1;
|
||||
}
|
||||
peek_index += 1;
|
||||
} while (peek_token.type !== TOKEN.EOF && peek_token.type !== TOKEN.TAG_CLOSE);
|
||||
}
|
||||
|
||||
//indent attributes an auto, forced, aligned or forced-align line-wrap
|
||||
if (this._is_wrap_attributes_force_aligned || this._is_wrap_attributes_aligned_multiple || this._is_wrap_attributes_preserve_aligned) {
|
||||
parser_token.alignment_size = raw_token.text.length + 1;
|
||||
|
@ -43,6 +43,7 @@ function Options(options) {
|
||||
this.indent_handlebars = this._get_boolean('indent_handlebars', true);
|
||||
this.wrap_attributes = this._get_selection('wrap_attributes',
|
||||
['auto', 'force', 'force-aligned', 'force-expand-multiline', 'aligned-multiple', 'preserve', 'preserve-aligned']);
|
||||
this.wrap_attributes_min_attrs = this._get_number('wrap_attributes_min_attrs', 2);
|
||||
this.wrap_attributes_indent_size = this._get_number('wrap_attributes_indent_size', this.indent_size);
|
||||
this.extra_liners = this._get_array('extra_liners', ['head', 'body', '/html']);
|
||||
|
||||
|
@ -1193,6 +1193,71 @@ exports.test_data = {
|
||||
'^^^indent_attr$$$(typeaheadOnSelect)="onSuggestionSelected($event)" />'
|
||||
]
|
||||
}]
|
||||
}, {
|
||||
name: "Test wrap_attributes_min_attrs with force/force-xx options",
|
||||
description: "",
|
||||
matrix: [{
|
||||
options: [
|
||||
{ name: "wrap_attributes", value: "'force'" },
|
||||
{ name: "wrap_attributes_min_attrs", value: "4" }
|
||||
],
|
||||
indent_attr: ' ',
|
||||
indent_attr_first: ' ',
|
||||
indent_end: ' ',
|
||||
newline_end: ''
|
||||
}, {
|
||||
options: [
|
||||
{ name: "wrap_attributes", value: "'force-aligned'" },
|
||||
{ name: "wrap_attributes_min_attrs", value: "4" }
|
||||
],
|
||||
indent_attr: ' ',
|
||||
indent_attr_first: ' ',
|
||||
indent_end: ' ',
|
||||
newline_end: ''
|
||||
}, {
|
||||
options: [
|
||||
{ name: "wrap_attributes", value: "'force-expand-multiline'" },
|
||||
{ name: "wrap_attributes_min_attrs", value: "4" }
|
||||
],
|
||||
indent_attr: ' ',
|
||||
indent_attr_first: '\n ',
|
||||
indent_end: '\n',
|
||||
newline_end: '\n'
|
||||
}],
|
||||
tests: [{
|
||||
input: [
|
||||
'<input type="text" class="form-control" autocomplete="off"',
|
||||
'[(ngModel)]="myValue" />'
|
||||
],
|
||||
output: [
|
||||
'<input{{indent_attr_first}}type="text"',
|
||||
'{{indent_attr}}class="form-control"',
|
||||
'{{indent_attr}}autocomplete="off"',
|
||||
'{{indent_attr}}[(ngModel)]="myValue"{{indent_end}}/>'
|
||||
]
|
||||
}, {
|
||||
input: [
|
||||
'<input type="text" autocomplete="off"',
|
||||
'[(ngModel)]="myValue" />'
|
||||
],
|
||||
output: '<input type="text" autocomplete="off" [(ngModel)]="myValue" />'
|
||||
}, {
|
||||
input: [
|
||||
'<cmpnt v-bind:xx="xx" ' +
|
||||
'@someevent="dosomething" someprop',
|
||||
'class="xx-button">',
|
||||
'<div class="alert alert-info" style="margin-left: 1px;" role="alert">lorem ipsum</div>',
|
||||
'</cmpnt>'
|
||||
],
|
||||
output: [
|
||||
'<cmpnt{{indent_attr_first}}v-bind:xx="xx"',
|
||||
'{{indent_attr}}@someevent="dosomething"',
|
||||
'{{indent_attr}}someprop',
|
||||
'{{indent_attr}}class="xx-button"{{newline_end}}>',
|
||||
' <div class="alert alert-info" style="margin-left: 1px;" role="alert">lorem ipsum</div>',
|
||||
'</cmpnt>'
|
||||
]
|
||||
}]
|
||||
}, {
|
||||
name: "Handlebars Indenting Off",
|
||||
description: "Test handlebar behavior when indenting is off",
|
||||
|
Loading…
x
Reference in New Issue
Block a user