Skip to content
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
4 changes: 4 additions & 0 deletions js/src/core/templatablepattern.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ TemplatablePattern.prototype.__set_templated_pattern = function() {
if (!this._disabled.handlebars) {
items.push(this.__patterns.handlebars._starting_pattern.source);
}
if (!this._disabled.angular) {
// Handlebars ('{{' and '}}') are also special tokens in Angular)
items.push(this.__patterns.handlebars._starting_pattern.source);
}
if (!this._disabled.erb) {
items.push(this.__patterns.erb._starting_pattern.source);
}
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
40 changes: 26 additions & 14 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 @@ -326,14 +327,29 @@ Tokenizer.prototype._is_content_unformatted = function(tag_name) {
this._options.unformatted.indexOf(tag_name) !== -1);
};


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'));
}
}

if (resulting_string) {
return this._create_token(TOKEN.TEXT, resulting_string);
}

return null;
};

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,17 +359,12 @@ 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)) {

resulting_string = this._input.readUntil(new RegExp('</' + tag_name + '[\\n\\r\\t ]*?>', 'ig'));
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);
}
}
}

if (resulting_string) {
return this._create_token(TOKEN.TEXT, resulting_string);
}

return null;
};

Expand All @@ -371,6 +382,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