diff --git a/js/src/core/templatablepattern.js b/js/src/core/templatablepattern.js index 4ada5694..047305e7 100644 --- a/js/src/core/templatablepattern.js +++ b/js/src/core/templatablepattern.js @@ -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) { diff --git a/js/src/html/beautifier.js b/js/src/html/beautifier.js index 32c882a5..e32940fb 100644 --- a/js/src/html/beautifier.js +++ b/js/src/html/beautifier.js @@ -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(); @@ -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'; @@ -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; diff --git a/js/src/html/tokenizer.js b/js/src/html/tokenizer.js index 764c60ae..1d918e8b 100644 --- a/js/src/html/tokenizer.js +++ b/js/src/html/tokenizer.js @@ -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); @@ -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); @@ -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; } @@ -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 @@ -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('', 'ig')); - } else if (this._is_content_unformatted(tag_name)) { + var resulting_string = this._input.readUntil(new RegExp('', '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('', 'ig')); } @@ -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; diff --git a/test/data/html/tests.js b/test/data/html/tests.js index 6e3d59ac..5595636e 100644 --- a/test/data/html/tests.js +++ b/test/data/html/tests.js @@ -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: [ @@ -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: [ - // '' - // ] - input: [ + unchanged: [ '' - ], - output: [ + ] + }, { + comment: 'CSS @media, the inside of ', + '', + '', + '
', + '@if(someOtherExpression) {', + 'Text', + '}', + '
', + '' + ], + output: [ + '', + ' ', + ' ', + '', + '', + '
', + ' @if(someOtherExpression) {', + ' Text', + ' }', + '
', + '' ] }] }, { - 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) {',