Skip to content

Commit

Permalink
Fixed #2260 - <style> and <script> tags indentation if also indenting…
Browse files Browse the repository at this point in the history
… angular control flows
  • Loading branch information
gergely-gyorgy-both committed Mar 29, 2024
1 parent 40c5356 commit d5176ef
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 47 deletions.
3 changes: 2 additions & 1 deletion js/src/core/templatablepattern.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ TemplatablePattern.prototype.__set_templated_pattern = function() {
if (!this._disabled.php) {
items.push(this.__patterns.php._starting_pattern.source);
}
if (!this._disabled.handlebars) {
// Handlebars ('{{' and '}}') are also special tokens in Angular)
if (!this._disabled.handlebars || !this._disabled.angular) {
items.push(this.__patterns.handlebars._starting_pattern.source);
}
if (!this._disabled.erb) {
Expand Down
9 changes: 5 additions & 4 deletions js/src/html/beautifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ Beautifier.prototype.beautify = function() {
type: ''
};

var last_tag_token = new TagOpenParserToken();
var last_tag_token = new TagOpenParserToken(this._options);

var printer = new Printer(this._options, baseIndentString);
var tokens = new Tokenizer(source_text, this._options).tokenize();
Expand Down Expand Up @@ -614,7 +614,7 @@ Beautifier.prototype._handle_tag_open = function(printer, raw_token, last_tag_to
return parser_token;
};

var TagOpenParserToken = function(parent, raw_token) {
var TagOpenParserToken = function(options, parent, raw_token) {
this.parent = parent || null;
this.text = '';
this.type = 'TK_TAG_OPEN';
Expand Down Expand Up @@ -681,13 +681,14 @@ var TagOpenParserToken = function(parent, raw_token) {
}

// handlebars tags that don't start with # or ^ are single_tags, and so also start and end.
// if they start with # or ^, they are still considered single tags if indenting of handlebars is set to false
this.is_end_tag = this.is_end_tag ||
(this.tag_start_char === '{' && (this.text.length < 3 || (/[^#\^]/.test(this.text.charAt(handlebar_starts)))));
(this.tag_start_char === '{' && (!options.indent_handlebars || this.text.length < 3 || (/[^#\^]/.test(this.text.charAt(handlebar_starts)))));
}
};

Beautifier.prototype._get_tag_open_token = function(raw_token) { //function to get a full tag and parse its type
var parser_token = new TagOpenParserToken(this._tag_stack.get_parser_token(), raw_token);
var parser_token = new TagOpenParserToken(this._options, this._tag_stack.get_parser_token(), raw_token);

parser_token.alignment_size = this._options.wrap_attributes_indent_size;

Expand Down
39 changes: 26 additions & 13 deletions js/src/html/tokenizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ Tokenizer.prototype._get_next_token = function(previous_token, open_token) { //
token = token || this._read_open_handlebars(c, open_token);
token = token || this._read_attribute(c, previous_token, open_token);
token = token || this._read_close(c, open_token);
token = token || this._read_script_and_style(c, previous_token);
token = token || this._read_control_flows(c, open_token);
token = token || this._read_raw_content(c, previous_token, open_token);
token = token || this._read_content_word(c, open_token);
Expand Down Expand Up @@ -215,8 +216,8 @@ Tokenizer.prototype._read_open_handlebars = function(c, open_token) {
var resulting_string = null;
var token = null;
if (!open_token || open_token.type === TOKEN.CONTROL_FLOW_OPEN) {
if (this._options.indent_handlebars && c === '{' && this._input.peek(1) === '{') {
if (this._input.peek(2) === '!') {
if ((this._options.templating.includes('angular') || this._options.indent_handlebars) && c === '{' && this._input.peek(1) === '{') {
if (this._options.indent_handlebars && this._input.peek(2) === '!') {
resulting_string = this.__patterns.handlebars_comment.read();
resulting_string = resulting_string || this.__patterns.handlebars.read();
token = this._create_token(TOKEN.COMMENT, resulting_string);
Expand All @@ -232,8 +233,8 @@ Tokenizer.prototype._read_open_handlebars = function(c, open_token) {
Tokenizer.prototype._read_control_flows = function(c, open_token) {
var resulting_string = '';
var token = null;
// Only check for control flows if angular templating is set AND indenting is set
if (!this._options.templating.includes('angular') || !this._options.indent_handlebars) {
// Only check for control flows if angular templating is set
if (!this._options.templating.includes('angular')) {
return token;
}

Expand Down Expand Up @@ -327,13 +328,8 @@ Tokenizer.prototype._is_content_unformatted = function(tag_name) {
};


Tokenizer.prototype._read_raw_content = function(c, previous_token, open_token) { // jshint unused:false
var resulting_string = '';
if (open_token && open_token.text[0] === '{') {
resulting_string = this.__patterns.handlebars_raw_close.read();
} else if (previous_token.type === TOKEN.TAG_CLOSE &&
previous_token.opened.text[0] === '<' && previous_token.text[0] !== '/') {
// ^^ empty tag has no content
Tokenizer.prototype._read_script_and_style = function(c, previous_token) { // jshint unused:false
if (previous_token.type === TOKEN.TAG_CLOSE && previous_token.opened.text[0] === '<' && previous_token.text[0] !== '/') {
var tag_name = previous_token.opened.text.substr(1).toLowerCase();
if (tag_name === 'script' || tag_name === 'style') {
// Script and style tags are allowed to have comments wrapping their content
Expand All @@ -343,8 +339,24 @@ Tokenizer.prototype._read_raw_content = function(c, previous_token, open_token)
token.type = TOKEN.TEXT;
return token;
}
resulting_string = this._input.readUntil(new RegExp('</' + tag_name + '[\\n\\r\\t ]*?>', 'ig'));
} else if (this._is_content_unformatted(tag_name)) {
var resulting_string = this._input.readUntil(new RegExp('</' + tag_name + '[\\n\\r\\t ]*?>', 'ig'));
if (resulting_string) {
return this._create_token(TOKEN.TEXT, resulting_string);
}
}
}
return null;
};

Tokenizer.prototype._read_raw_content = function(c, previous_token, open_token) { // jshint unused:false
var resulting_string = '';
if (open_token && open_token.text[0] === '{') {
resulting_string = this.__patterns.handlebars_raw_close.read();
} else if (previous_token.type === TOKEN.TAG_CLOSE &&
previous_token.opened.text[0] === '<' && previous_token.text[0] !== '/') {
// ^^ empty tag has no content
var tag_name = previous_token.opened.text.substr(1).toLowerCase();
if (this._is_content_unformatted(tag_name)) {

resulting_string = this._input.readUntil(new RegExp('</' + tag_name + '[\\n\\r\\t ]*?>', 'ig'));
}
Expand All @@ -371,6 +383,7 @@ Tokenizer.prototype._read_content_word = function(c, open_token) {
if (resulting_string) {
return this._create_token(TOKEN.TEXT, resulting_string);
}
return null;
};

module.exports.Tokenizer = Tokenizer;
Expand Down
80 changes: 51 additions & 29 deletions test/data/html/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -3916,7 +3916,7 @@ exports.test_data = {
description: "https://github.com/beautify-web/js-beautify/issues/2219",
template: "^^^ $$$",
options: [
{ name: "templating", value: "'angular, handlebars'" }
{ name: "templating", value: "'angular'" }
],
tests: [{
input: [
Expand Down Expand Up @@ -4240,18 +4240,7 @@ exports.test_data = {
]
}, {
comment: 'CSS @media should remain unchanged',
// This behavior is currently incorrect. This codifies the way it fails.
// unchanged: [
// '<style type="text/css">',
// ' @media only screen and (min-width:480px) {',
// ' .mj-column-per-100 {',
// ' width: 100% !important;',
// ' max-width: 100%;',
// ' }',
// ' }',
// '</style>'
// ]
input: [
unchanged: [
'<style type="text/css">',
' @media only screen and (min-width:480px) {',
' .mj-column-per-100 {',
Expand All @@ -4260,30 +4249,63 @@ exports.test_data = {
' }',
' }',
'</style>'
],
output: [
]
}, {
comment: 'CSS @media, the inside of <script> tag and control flows should be indented correctly',
fragment: true,
input: [
'<head>',
'<style type="text/css">',
'@media only screen and (min-width:480px) {',
' .mj-column-per-100',
' {',
' width:',
' 100%',
' !important;',
' max-width:',
' 100%;',
'.mj-column-per-100 {',
'width: 100% !important;',
'max-width: 100%;',
'}',
' }',
'</style>'
'}',
'</style>',
'<script>',
'if(someExpression) {',
'callFunc();',
'}',
'</script>',
'</head>',
'<body>',
'<div>',
'@if(someOtherExpression) {',
'Text',
'}',
'</div>',
'</body>'
],
output: [
'<head>',
' <style type="text/css">',
' @media only screen and (min-width:480px) {',
' .mj-column-per-100 {',
' width: 100% !important;',
' max-width: 100%;',
' }',
' }',
' </style>',
' <script>',
' if (someExpression) {',
' callFunc();',
' }',
' </script>',
'</head>',
'<body>',
' <div>',
' @if(someOtherExpression) {',
' Text',
' }',
' </div>',
'</body>'
]
}]
}, {
name: "No indenting for angular control flow should be done if indent_handlebars is false",
name: "No indenting for angular control flow should be done if angular templating is not set",
description: "https://github.com/beautify-web/js-beautify/issues/2219",
template: "^^^ $$$",
options: [
{ name: "templating", value: "'angular, handlebars'" },
{ name: "indent_handlebars", value: "false" }
],
tests: [{
unchanged: [
'@if (a > b) {',
Expand Down

0 comments on commit d5176ef

Please sign in to comment.