diff --git a/README.md b/README.md index 4d2808657..d5b8a8d88 100644 --- a/README.md +++ b/README.md @@ -216,7 +216,7 @@ HTML Beautifier Options: -b, --brace-style [collapse-preserve-inline|collapse|expand|end-expand|none] ["collapse"] -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] ["auto"] + -A, --wrap-attributes Wrap attributes to new lines [auto|force|force-aligned|force-expand-multiline] ["auto"] -i, --wrap-attributes-indent-size Indent wrapped attributes to after N characters [indent-size] (ignored if wrap-attributes is "force-aligned") -U, --unformatted List of tags (defaults to inline) that should not be reformatted -E, --extra_liners List of tags (defaults to [head,body,/html] that should have an extra newline before them. diff --git a/js/lib/beautify-html.js b/js/lib/beautify-html.js index c21b813e7..ab02c44b2 100644 --- a/js/lib/beautify-html.js +++ b/js/lib/beautify-html.js @@ -102,6 +102,8 @@ wrap_attributes, wrap_attributes_indent_size, is_wrap_attributes_force, + is_wrap_attributes_force_expand_multiline, + is_wrap_attributes_force_aligned, end_with_newline, extra_liners, eol; @@ -141,6 +143,8 @@ wrap_attributes = (options.wrap_attributes === undefined) ? 'auto' : options.wrap_attributes; wrap_attributes_indent_size = (isNaN(parseInt(options.wrap_attributes_indent_size, 10))) ? indent_size : parseInt(options.wrap_attributes_indent_size, 10); is_wrap_attributes_force = wrap_attributes.substr(0, 'force'.length) === 'force'; + is_wrap_attributes_force_expand_multiline = (wrap_attributes === 'force-expand-multiline'); + is_wrap_attributes_force_aligned = (wrap_attributes === 'force-aligned'); end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline; extra_liners = (typeof options.extra_liners === 'object') && options.extra_liners ? options.extra_liners.concat() : (typeof options.extra_liners === 'string') ? @@ -361,10 +365,13 @@ comment = '', space = false, first_attr = true, + has_wrapped_attrs = false, tag_start, tag_end, tag_start_char, orig_pos = this.pos, - orig_line_char_count = this.line_char_count; + orig_line_char_count = this.line_char_count, + is_tag_closed = false, + tail; peek = peek !== undefined ? peek : false; @@ -388,27 +395,44 @@ if (input_char === "'" || input_char === '"') { input_char += this.get_unformatted(input_char); space = true; - } if (input_char === '=') { //no space before = space = false; } - + tail = this.input.substr(this.pos - 1); + if (is_wrap_attributes_force_expand_multiline && has_wrapped_attrs && !is_tag_closed && (input_char === '>' || input_char === '/')) { + if (tail.match(/^\/?\s*>/)) { + space = false; + is_tag_closed = true; + this.print_newline(false, content); + this.print_indentation(content); + } + } if (content.length && content[content.length - 1] !== '=' && input_char !== '>' && space) { //no space after = or before > var wrapped = this.space_or_wrap(content); var indentAttrs = wrapped && input_char !== '/' && !is_wrap_attributes_force; space = false; - if (!first_attr && is_wrap_attributes_force && input_char !== '/') { - this.print_newline(false, content); - this.print_indentation(content); - indentAttrs = true; + + if (is_wrap_attributes_force && input_char !== '/') { + var force_first_attr_wrap = false; + if (is_wrap_attributes_force_expand_multiline && first_attr) { + var is_only_attribute = tail.match(/^\S*(="([^"]|\\")*")?\s*\/?\s*>/) !== null; + force_first_attr_wrap = !is_only_attribute; + } + if (!first_attr || force_first_attr_wrap) { + this.print_newline(false, content); + this.print_indentation(content); + indentAttrs = true; + } } if (indentAttrs) { + has_wrapped_attrs = true; + //indent attributes an auto, forced, or forced-align line-wrap var alignment_size = wrap_attributes_indent_size; - if (wrap_attributes === 'force-aligned') { + if (is_wrap_attributes_force_aligned) { alignment_size = content.indexOf(' ') + 1; } @@ -416,10 +440,12 @@ content.push(indent_character); } } - for (var i = 0; i < content.length; i++) { - if (content[i] === ' ') { - first_attr = false; - break; + if (first_attr) { + for (var i = 0; i < content.length; i++) { + if (content[i] === ' ') { + first_attr = false; + break; + } } } } @@ -478,7 +504,9 @@ var tag_index; var tag_offset; - if (tag_complete.indexOf(' ') !== -1) { //if there's whitespace, thats where the tag name ends + if (tag_complete.indexOf('\n') !== -1) { //if there's a line break, thats where the tag name ends + tag_index = tag_complete.indexOf('\n'); + } else if (tag_complete.indexOf(' ') !== -1) { //if there's whitespace, thats where the tag name ends tag_index = tag_complete.indexOf(' '); } else if (tag_complete.charAt(0) === '{') { tag_index = tag_complete.indexOf('}'); diff --git a/js/test/generated/beautify-html-tests.js b/js/test/generated/beautify-html-tests.js index f16f0466f..88fd38f3d 100644 --- a/js/test/generated/beautify-html-tests.js +++ b/js/test/generated/beautify-html-tests.js @@ -258,87 +258,139 @@ function run_html_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_be reset_options(); //============================================================ - // Attribute Wrap - (indent_attr = "\n ", indent_over80 = "\n ") + // Attribute Wrap - (indent_attr = "\n ", indent_attr_first = " ", indent_end = "", indent_end_selfclosing = " ", indent_over80 = "\n ") opts.wrap_attributes = 'force'; + test_fragment('
This is some text
', '
This is some text
'); + test_fragment('
This is some text
', '
This is some text
'); test_fragment('
This is some text
', '
This is some text
'); test_fragment('
This is some text
', '
This is some text
'); test_fragment('', ''); test_fragment('', '\n'); test_fragment('', ''); - // Attribute Wrap - (indent_attr = "\n ", indent_over80 = "\n ") + // Attribute Wrap - (indent_attr = "\n ", indent_attr_first = " ", indent_end = "", indent_end_selfclosing = " ", indent_over80 = "\n ") opts.wrap_attributes = 'force'; opts.wrap_line_length = 80; + test_fragment('
This is some text
', '
This is some text
'); + test_fragment('
This is some text
', '
This is some text
'); test_fragment('
This is some text
', '
This is some text
'); test_fragment('
This is some text
', '
This is some text
'); test_fragment('', ''); test_fragment('', '\n'); test_fragment('', ''); - // Attribute Wrap - (indent_attr = "\n ", indent_over80 = "\n ") + // Attribute Wrap - (indent_attr = "\n ", indent_attr_first = " ", indent_end = "", indent_end_selfclosing = " ", indent_over80 = "\n ") opts.wrap_attributes = 'force'; opts.wrap_attributes_indent_size = 8; + test_fragment('
This is some text
', '
This is some text
'); + test_fragment('
This is some text
', '
This is some text
'); test_fragment('
This is some text
', '
This is some text
'); test_fragment('
This is some text
', '
This is some text
'); test_fragment('', ''); test_fragment('', '\n'); test_fragment('', ''); - // Attribute Wrap - (indent_attr = " ", indent_over80 = "\n") + // Attribute Wrap - (indent_attr = " ", indent_attr_first = " ", indent_end = "", indent_end_selfclosing = " ", indent_over80 = "\n") opts.wrap_attributes = 'auto'; opts.wrap_line_length = 80; opts.wrap_attributes_indent_size = 0; + test_fragment('
This is some text
', '
This is some text
'); + test_fragment('
This is some text
', '
This is some text
'); test_fragment('
This is some text
'); test_fragment('
This is some text
', '
This is some text
'); test_fragment('', ''); test_fragment('', '\n'); test_fragment('', ''); - // Attribute Wrap - (indent_attr = " ", indent_over80 = "\n ") + // Attribute Wrap - (indent_attr = " ", indent_attr_first = " ", indent_end = "", indent_end_selfclosing = " ", indent_over80 = "\n ") opts.wrap_attributes = 'auto'; opts.wrap_line_length = 80; opts.wrap_attributes_indent_size = 4; + test_fragment('
This is some text
', '
This is some text
'); + test_fragment('
This is some text
', '
This is some text
'); test_fragment('
This is some text
'); test_fragment('
This is some text
', '
This is some text
'); test_fragment('', ''); test_fragment('', '\n'); test_fragment('', ''); - // Attribute Wrap - (indent_attr = " ", indent_over80 = " ") + // Attribute Wrap - (indent_attr = " ", indent_attr_first = " ", indent_end = "", indent_end_selfclosing = " ", indent_over80 = " ") opts.wrap_attributes = 'auto'; opts.wrap_line_length = 0; + test_fragment('
This is some text
', '
This is some text
'); + test_fragment('
This is some text
', '
This is some text
'); test_fragment('
This is some text
'); test_fragment('
This is some text
'); test_fragment('', ''); test_fragment('', '\n'); test_fragment(''); - // Attribute Wrap - (indent_attr = "\n ", indent_attr_faligned = " ", indent_over80 = "\n ") + // Attribute Wrap - (indent_attr = "\n ", indent_attr_faligned = " ", indent_attr_first = " ", indent_end = "", indent_end_selfclosing = " ", indent_over80 = "\n ") opts.wrap_attributes = 'force-aligned'; + test_fragment('
This is some text
', '
This is some text
'); + test_fragment('
This is some text
', '
This is some text
'); test_fragment('
This is some text
', '
This is some text
'); test_fragment('
This is some text
', '
This is some text
'); test_fragment('', ''); test_fragment('', '\n'); test_fragment('', ''); - // Attribute Wrap - (indent_attr = "\n ", indent_attr_faligned = " ", indent_over80 = "\n ") + // Attribute Wrap - (indent_attr = "\n ", indent_attr_faligned = " ", indent_attr_first = " ", indent_end = "", indent_end_selfclosing = " ", indent_over80 = "\n ") opts.wrap_attributes = 'force-aligned'; opts.wrap_line_length = 80; + test_fragment('
This is some text
', '
This is some text
'); + test_fragment('
This is some text
', '
This is some text
'); test_fragment('
This is some text
', '
This is some text
'); test_fragment('
This is some text
', '
This is some text
'); test_fragment('', ''); test_fragment('', '\n'); test_fragment('', ''); - // Attribute Wrap - (indent_attr = "\n ", indent_attr_faligned = " ", indent_over80 = "\n ") + // Attribute Wrap - (indent_attr = "\n ", indent_attr_faligned = " ", indent_attr_first = " ", indent_end = "", indent_end_selfclosing = " ", indent_over80 = "\n ") opts.wrap_attributes = 'force-aligned'; opts.wrap_attributes_indent_size = 8; + test_fragment('
This is some text
', '
This is some text
'); + test_fragment('
This is some text
', '
This is some text
'); test_fragment('
This is some text
', '
This is some text
'); test_fragment('
This is some text
', '
This is some text
'); test_fragment('', ''); test_fragment('', '\n'); test_fragment('', ''); + // Attribute Wrap - (indent_attr = "\n ", indent_attr_first = "\n ", indent_end = "\n", indent_end_selfclosing = "\n", indent_over80 = "\n ") + opts.wrap_attributes = 'force-expand-multiline'; + opts.wrap_attributes_indent_size = 4; + test_fragment('
This is some text
', '
This is some text
'); + test_fragment('
This is some text
', '
This is some text
'); + test_fragment('
This is some text
', 'This is some text'); + test_fragment('
This is some text
', 'This is some text'); + test_fragment('', ''); + test_fragment('', '\n'); + test_fragment('', ''); + + // Attribute Wrap - (indent_attr = "\n ", indent_attr_first = "\n ", indent_end = "\n", indent_end_selfclosing = "\n", indent_over80 = "\n ") + opts.wrap_attributes = 'force-expand-multiline'; + opts.wrap_attributes_indent_size = 4; + opts.wrap_line_length = 80; + test_fragment('
This is some text
', '
This is some text
'); + test_fragment('
This is some text
', '
This is some text
'); + test_fragment('
This is some text
', 'This is some text'); + test_fragment('
This is some text
', 'This is some text'); + test_fragment('', ''); + test_fragment('', '\n'); + test_fragment('', ''); + + // Attribute Wrap - (indent_attr = "\n ", indent_attr_first = "\n ", indent_end = "\n", indent_end_selfclosing = "\n", indent_over80 = "\n ") + opts.wrap_attributes = 'force-expand-multiline'; + opts.wrap_attributes_indent_size = 8; + test_fragment('
This is some text
', '
This is some text
'); + test_fragment('
This is some text
', '
This is some text
'); + test_fragment('
This is some text
', 'This is some text'); + test_fragment('
This is some text
', 'This is some text'); + test_fragment('', ''); + test_fragment('', '\n'); + test_fragment('', ''); + reset_options(); //============================================================ diff --git a/test/data/html/tests.js b/test/data/html/tests.js index 9f390732a..017c44f6a 100644 --- a/test/data/html/tests.js +++ b/test/data/html/tests.js @@ -262,6 +262,9 @@ exports.test_data = { { name: "wrap_attributes", value: "'force'" } ], indent_attr: '\\n ', + indent_attr_first: ' ', + indent_end: '', + indent_end_selfclosing: ' ', indent_over80: '\\n ' }, { options: [ @@ -269,6 +272,9 @@ exports.test_data = { { name: "wrap_line_length", value: "80" } ], indent_attr: '\\n ', + indent_attr_first: ' ', + indent_end: '', + indent_end_selfclosing: ' ', indent_over80: '\\n ' }, { options: [ @@ -276,6 +282,9 @@ exports.test_data = { { name: "wrap_attributes_indent_size", value: "8" } ], indent_attr: '\\n ', + indent_attr_first: ' ', + indent_end: '', + indent_end_selfclosing: ' ', indent_over80: '\\n ' }, { options: [ @@ -284,6 +293,9 @@ exports.test_data = { { name: "wrap_attributes_indent_size", value: "0" } ], indent_attr: ' ', + indent_attr_first: ' ', + indent_end: '', + indent_end_selfclosing: ' ', indent_over80: '\\n' }, { options: [ @@ -292,6 +304,9 @@ exports.test_data = { { name: "wrap_attributes_indent_size", value: "4" } ], indent_attr: ' ', + indent_attr_first: ' ', + indent_end: '', + indent_end_selfclosing: ' ', indent_over80: '\\n ' }, { options: [ @@ -299,6 +314,9 @@ exports.test_data = { { name: "wrap_line_length", value: "0" } ], indent_attr: ' ', + indent_attr_first: ' ', + indent_end: '', + indent_end_selfclosing: ' ', indent_over80: ' ' }, { options: [ @@ -306,6 +324,9 @@ exports.test_data = { ], indent_attr: '\\n ', indent_attr_faligned: ' ', + indent_attr_first: ' ', + indent_end: '', + indent_end_selfclosing: ' ', indent_over80: '\\n ' }, { options: [ @@ -314,6 +335,9 @@ exports.test_data = { ], indent_attr: '\\n ', indent_attr_faligned: ' ', + indent_attr_first: ' ', + indent_end: '', + indent_end_selfclosing: ' ', indent_over80: '\\n ' }, { options: [ @@ -322,28 +346,70 @@ exports.test_data = { ], indent_attr: '\\n ', indent_attr_faligned: ' ', + indent_attr_first: ' ', + indent_end: '', + indent_end_selfclosing: ' ', indent_over80: '\\n ' + }, { + options: [ + { name: "wrap_attributes", value: "'force-expand-multiline'" }, + { name: "wrap_attributes_indent_size", value: "4" } + ], + indent_attr: '\\n ', + indent_attr_first: '\\n ', + indent_end: '\\n', + indent_end_selfclosing: '\\n', + indent_over80: '\\n ' + }, { + options: [ + { name: "wrap_attributes", value: "'force-expand-multiline'" }, + { name: "wrap_attributes_indent_size", value: "4" }, + { name: "wrap_line_length", value: "80" } + ], + indent_attr: '\\n ', + indent_attr_first: '\\n ', + indent_end: '\\n', + indent_end_selfclosing: '\\n', + indent_over80: '\\n ' + }, { + options: [ + { name: "wrap_attributes", value: "'force-expand-multiline'" }, + { name: "wrap_attributes_indent_size", value: "8" } + ], + indent_attr: '\\n ', + indent_attr_first: '\\n ', + indent_end: '\\n', + indent_end_selfclosing: '\\n', + indent_over80: '\\n ' }], tests: [{ + fragment: true, + input: '
This is some text
', + output: '
This is some text
' + }, { + fragment: true, + input: '
This is some text
', + output: '
This is some text
' + }, { fragment: true, input: '
This is some text
', - output: '
This is some text
' + output: 'This is some text' }, { fragment: true, input: '
This is some text
', - output: '
This is some text
' + output: 'This is some text' }, { fragment: true, input: '', - output: '' + output: '' }, { fragment: true, input: '', - output: '\n' + output: '\n' }, { fragment: true, input: '', - output: '' + output: '' }] }, { name: "Handlebars Indenting Off",