From a5ca809979ace5b27a63788f6b6770e416f70fba Mon Sep 17 00:00:00 2001 From: Marc Laval Date: Wed, 10 Sep 2014 18:52:58 +0200 Subject: [PATCH] White space management --- hsp/compiler/jsgenerator/processors.js | 2 +- hsp/compiler/parser/hspblocks.pegjs | 42 +++++++++---------- hsp/compiler/treebuilder/syntaxTree.js | 58 +++++++++++++++++++++++--- 3 files changed, 73 insertions(+), 29 deletions(-) diff --git a/hsp/compiler/jsgenerator/processors.js b/hsp/compiler/jsgenerator/processors.js index 4c2d736..391bce8 100644 --- a/hsp/compiler/jsgenerator/processors.js +++ b/hsp/compiler/jsgenerator/processors.js @@ -526,7 +526,7 @@ function formatTextBlock (node, nextExprIndex, walker) { // (=args) for (var i = 0; i < content.length; i++) { item = content[i]; - if (item.type === "text") { + if (item.type === "text" || item.type === "eol") { if (index % 2 === 0) { // even index: arg must be a string args[index] = '"' + escapeNewLines(item.value.replace(/"/g, "\\\"")) + '"'; diff --git a/hsp/compiler/parser/hspblocks.pegjs b/hsp/compiler/parser/hspblocks.pegjs index 71e0e94..844d285 100644 --- a/hsp/compiler/parser/hspblocks.pegjs +++ b/hsp/compiler/parser/hspblocks.pegjs @@ -75,7 +75,7 @@ TemplateEnd2 {return {type:"/template",line:line,column:column}} TemplateContent "template content" // TODO: CSSClassExpression - = _ blocks:( TplTextBlock + = blocks:( EOLBlock / TplTextBlock / CommentBlock / HTMLCommentBlock / IfBlock / ElseIfBlock / ElseBlock / EndIfBlock / ForeachBlock / EndForeachBlock @@ -96,41 +96,38 @@ TplTextBlock "text" TplTextChar "text character" = "\\{" {return "\u007B"} // { = \u007B / "\\}" {return "\u007D"} // } = \u007D - / "\\n" {return "\n"} - / EOL &TemplateEnd {return ""} // ignore last EOL - / EOL _ {return " "} / "#" !(_ "\/template") {return "#"} / "\/" !"/" {return "/"} / "\\/" {return "/"} / "\\//" {return "//"} / "\\<" {return "<"} - / [^{#/<] + / [^{#/<\n\r\u2028\u2029] InvalidBlock = "{" !(_ "/template" _ "}") chars:[^{}#]* "}" {return {type:"invalidblock", code:chars.join(''), line:line, column:column}} IfBlock "if statement" - = "{" _ "if " _ expr:(IfCondWithBrackets / CoreExpText) _ "}" EOS? + = "{" _ "if " _ expr:(IfCondWithBrackets / CoreExpText) _ "}" {return {type:"if", condition:expr, line:line, column:column}} IfCondWithBrackets = "(" expr:CoreExpText ")" {return expr} ElseIfBlock "elseif statement" - = "{" _ "else " _ "if" _ expr:(IfCondWithBrackets / CoreExpText) _ "}" EOS? + = "{" _ "else " _ "if" _ expr:(IfCondWithBrackets / CoreExpText) _ "}" {return {type:"elseif", condition:expr, line:line, column:column}} ElseBlock - = "{" _ "else" _ "}" EOS? + = "{" _ "else" _ "}" {return {type:"else", line:line, column:column}} EndIfBlock - = "{" _ "/if" _ "}" EOS? + = "{" _ "/if" _ "}" {return {type:"endif", line:line, column:column}} CommentBlock - = _ "\/\/" chars:[^\r\n]* &EOL + = _ "\/\/" chars:[^\r\n]* {return {type:"comment", value:chars.join('')}} HTMLCommentBlock @@ -144,7 +141,7 @@ HTMLCommentChar / [^>\-] ForeachBlock - = "{" _ "foreach " _ args:( ForeachArgs / ("(" _ a:ForeachArgs _ ")") {return a}) _ "}" EOS? + = "{" _ "foreach " _ args:( ForeachArgs / ("(" _ a:ForeachArgs _ ")") {return a}) _ "}" {return {type:"foreach", item:args.item, key:args.key, colref:args.colref, line:line, column:column}} ForeachArgs @@ -163,30 +160,30 @@ EndForeachBlock {return {type:"endforeach", line:line, column:column}} HTMLElement - = "<" name:HTMLName atts:HTMLElementAttributes? S? end:"/"? ">" EOS? + = "<" name:HTMLName atts:HTMLElementAttributes? S? end:"/"? ">" {return {type:"element", name:name, closed:(end!==""), attributes:atts, line:line, column:column}} HTMLElementAttributes = atts:((S att:(HTMLAttribute)) {return att})* EndHTMLElement // TODO support comments inside Element - = "" EOS? + = "" {return {type:"endelement", name:name, line:line, column:column}} HspComponent - = "<#" ref:JSObjectRef atts:HTMLElementAttributes? S? end:"/"? ">" EOS? + = "<#" ref:JSObjectRef atts:HTMLElementAttributes? S? end:"/"? ">" {return {type:"component", ref:ref, closed:(end!==""), attributes:atts, line:line, column:column}} EndHspComponent - = "" EOS? + = "" {return {type:"endcomponent", ref:ref, line:line, column:column}} HspCptAttribute - = "<@" ref:VarIdentifier atts:HTMLElementAttributes? S? end:"/"? ">" EOS? + = "<@" ref:VarIdentifier atts:HTMLElementAttributes? S? end:"/"? ">" {return {type:"cptattribute", name:ref, closed:(end!==""), attributes:atts, line:line, column:column}} EndHspCptAttribute - = "" EOS? + = "" {return {type:"endcptattribute", name:ref, line:line, column:column}} InvalidHTMLElement @@ -221,7 +218,7 @@ HTMLAttributeChar // TODO look at W3C specs / [^{\"\n\r] LogBlock - = "{" _ "log " _ first:CoreExpText _ next:("," _ CoreExpText)* _"}" EOS? + = "{" _ "log " _ first:CoreExpText _ next:("," _ CoreExpText)* _"}" { var exprs=[first]; if (next) { @@ -233,7 +230,7 @@ LogBlock } LetBlock - = "{" _ "let " _ first:CoreExpText __ next:("," __ CoreExpText)* "}" EOS? + = "{" _ "let " _ first:CoreExpText __ next:("," __ CoreExpText)* "}" { var asn=[first]; if (next) { @@ -365,6 +362,10 @@ InvalidExpressionValue = !("/template" _) chars:[^}]+ {return {type:"invalidexpression", code:chars.join(''), line:line, column:column}} +EOLBlock "End of line block" + = eol:EOL + {return {type:"eol", value:eol, line:line, column:column};} + // White spaces // mandatory padding including line breaks S "white space" @@ -383,9 +384,6 @@ EOL "end of line" / "\u2028" // line separator / "\u2029" // paragraph separator -EOS "end of statement" // - = empty:(_ EOL _) - EOF "end of file" = !. diff --git a/hsp/compiler/treebuilder/syntaxTree.js b/hsp/compiler/treebuilder/syntaxTree.js index 44e6d9b..fd54785 100644 --- a/hsp/compiler/treebuilder/syntaxTree.js +++ b/hsp/compiler/treebuilder/syntaxTree.js @@ -277,6 +277,43 @@ var SyntaxTree = klass({ return index; }, + /** + * Manages a End Of Line block + * @param {Array} blocks the full list of blocks. + * @param {Integer} index the index of the block to manage. + * @param {Array} out the output as an array of Node. + * @return {Integer} the index of the block where the function stopped or -1 if all blocks have been handled. + */ + __eol : function(index, blocks, out) { + return this.__text(index, blocks, out); + }, + + _skipWhitespaces: function(index, blocks, out) { + //Skips opening whitespace block and ending EOL block if the line is only made of blocks which don't generate DOM element or text + var toBeSkipped = true; + var nextIndex = index + 1; + while (nextIndex < blocks.length) { + var nextBlock = blocks[nextIndex]; + if (!(nextBlock.type === "text" && nextBlock.value.match(/^(\s)+$/) || + ["if", "elseif", "else", "endif", "comment", "foreach", "endforeach", "eol", "log", "let"].indexOf(nextBlock.type) > -1)) { + toBeSkipped = false; + break; + } + if (nextBlock.type === "eol") { + break; + } + nextIndex++; + } + if (index + 1 < blocks.length && toBeSkipped) { + if (blocks[index + 1].type === "text") { + blocks[index + 1].toBeSkipped = true; + } + if (blocks[nextIndex].type === "eol") { + blocks[nextIndex].toBeSkipped = true; + } + } + }, + /** * Manages a text block: regroups adjacent text and expression blocks * @param {Array} blocks the full list of blocks. @@ -287,19 +324,28 @@ var SyntaxTree = klass({ __text : function (index, blocks, out) { var length = blocks.length, buffer = []; + if (index === 0) { + this._skipWhitespaces(-1, blocks, out); + } + //Regroups adjacent text and expression blocks by looking at the next ones var nextIndex = index, goAhead = (length > nextIndex), block; while (goAhead) { block = blocks[nextIndex]; - if (block.type === "text") { + if (block.type === "text" || block.type === "eol") { if (block.value !== "") { try { - block.value = htmlEntitiesToUtf8(block.value); - buffer.push(block); + block.value = htmlEntitiesToUtf8(block.value); + if (typeof block.toBeSkipped === "undefined") { + buffer.push(block); + } } catch (e) { - this._logError(e.message, block); + this._logError(e.message, block); } } + if (block.type === "eol") { + this._skipWhitespaces(nextIndex, blocks, out); + } } else if (block.type === "expression") { if (block.category === "jsexpression") { // pre-process expression @@ -327,7 +373,7 @@ var SyntaxTree = klass({ //Manages the adjacent text and expression blocks found var node = null; - if (buffer.length === 1 && buffer[0].type === "text") { + if (buffer.length === 1 && (buffer[0].type === "text" || buffer[0].type === "eol")) { // only one text block node = new Node("text"); node.value = buffer[0].value; @@ -335,7 +381,7 @@ var SyntaxTree = klass({ // if buffer is composed of only text expressions we concatenate them var onlyText=true; for (var i = 0; i < buffer.length; i++) { - if (buffer[i].type !== "text") { + if (buffer[i].type !== "text" && buffer[i].type !== "eol") { onlyText = false; break; }