From 954253305a0deadf25236e087dd4d2908aee715a Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Thu, 11 Jul 2013 12:28:37 -0700 Subject: [PATCH 1/3] Add {{{{ }}}} for raw blocks --- spec/blocks.js | 8 ++++++++ src/handlebars.l | 5 +++++ src/handlebars.yy | 3 ++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/spec/blocks.js b/spec/blocks.js index d72a44ec4..b9672af87 100644 --- a/spec/blocks.js +++ b/spec/blocks.js @@ -21,6 +21,14 @@ describe('blocks', function() { equal(result, "0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!", "The @index variable is used"); }); + + it("raw block", function() { + var string = "{{{{ {{test}} }}}}"; + var hash = { test: "hello" }; + shouldCompileTo(string, hash, " {{test}} ", + "raw block ignores blocks"); + }); + it("empty block", function() { var string = "{{#goodbyes}}{{/goodbyes}}cruel {{world}}!"; var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"}; diff --git a/src/handlebars.l b/src/handlebars.l index 630840eb5..51e921aec 100644 --- a/src/handlebars.l +++ b/src/handlebars.l @@ -54,6 +54,11 @@ ID [^\s!"#%-,\.\/;->@\[-\^`\{-~]+/{LOOKAHEAD} "(" return 'OPEN_SEXPR'; ")" return 'CLOSE_SEXPR'; +"{{{{"[^\x00]*"}}}}" { + yytext = yytext.substr(4, yyleng-8); + this.popState(); + return 'RAW_BLOCK'; + } "{{"{LEFT_STRIP}?">" return 'OPEN_PARTIAL'; "{{"{LEFT_STRIP}?"#" return 'OPEN_BLOCK'; "{{"{LEFT_STRIP}?"/" return 'OPEN_ENDBLOCK'; diff --git a/src/handlebars.yy b/src/handlebars.yy index 40f68ce59..8b5b77455 100644 --- a/src/handlebars.yy +++ b/src/handlebars.yy @@ -35,7 +35,8 @@ statements ; statement - : openInverse program closeBlock -> new yy.BlockNode($1, $2.inverse, $2, $3, @$) + : RAW_BLOCK { $$ = new yy.ContentNode($1, @$); } + | openInverse program closeBlock -> new yy.BlockNode($1, $2.inverse, $2, $3, @$) | openBlock program closeBlock -> new yy.BlockNode($1, $2, $2.inverse, $3, @$) | mustache -> $1 | partial -> $1 From 9b14dc40a5ff5c3db0b62278dc0ea794c9a16593 Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Thu, 11 Jul 2013 14:50:14 -0700 Subject: [PATCH 2/3] raw block helpers --- lib/handlebars/compiler/ast.js | 9 +++++++++ spec/blocks.js | 8 -------- spec/helpers.js | 20 ++++++++++++++++++++ src/handlebars.l | 7 ++++++- src/handlebars.yy | 6 +++++- 5 files changed, 40 insertions(+), 10 deletions(-) diff --git a/lib/handlebars/compiler/ast.js b/lib/handlebars/compiler/ast.js index 5a5f514db..b9e67d7fe 100644 --- a/lib/handlebars/compiler/ast.js +++ b/lib/handlebars/compiler/ast.js @@ -131,6 +131,15 @@ var AST = { } }, + RawBlockNode: function(mustache, content, locInfo) { + LocationInfo.call(this, locInfo); + + this.type = 'block'; + this.mustache = mustache; + mustache.sexpr.isHelper = mustache.isHelper = true; + mustache.params.splice(0, 0, new AST.StringNode(content, locInfo)); + }, + ContentNode: function(string, locInfo) { LocationInfo.call(this, locInfo); this.type = "content"; diff --git a/spec/blocks.js b/spec/blocks.js index b9672af87..8f7c242fa 100644 --- a/spec/blocks.js +++ b/spec/blocks.js @@ -20,14 +20,6 @@ describe('blocks', function() { equal(result, "0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!", "The @index variable is used"); }); - - - it("raw block", function() { - var string = "{{{{ {{test}} }}}}"; - var hash = { test: "hello" }; - shouldCompileTo(string, hash, " {{test}} ", - "raw block ignores blocks"); - }); it("empty block", function() { var string = "{{#goodbyes}}{{/goodbyes}}cruel {{world}}!"; diff --git a/spec/helpers.js b/spec/helpers.js index 904f56a7c..b1d972291 100644 --- a/spec/helpers.js +++ b/spec/helpers.js @@ -9,6 +9,26 @@ describe('helpers', function() { shouldCompileTo(string, [hash, helpers], "Goodbye"); }); + it("helper for raw block gets raw content", function() { + var string = "{{{{raw}}}} {{test}} {{{{/raw}}}}"; + var hash = { test: "hello" }; + var helpers = { raw: function(content) { + return content; + } }; + shouldCompileTo(string, [hash, helpers], " {{test}} ", + "raw block helper gets raw content"); + }); + + it("helper for raw block gets parameters", function() { + var string = "{{{{raw 1 2 3}}}} {{test}} {{{{/raw}}}}"; + var hash = { test: "hello" }; + var helpers = { raw: function(content, a, b, c) { + return content + a + b + c; + } }; + shouldCompileTo(string, [hash, helpers], " {{test}} 123", + "raw block helper gets raw content"); + }); + it("helper block with complex lookup expression", function() { var string = "{{#goodbyes}}{{../name}}{{/goodbyes}}"; var hash = {name: "Alan"}; diff --git a/src/handlebars.l b/src/handlebars.l index 51e921aec..9b1266526 100644 --- a/src/handlebars.l +++ b/src/handlebars.l @@ -1,5 +1,5 @@ -%x mu emu com +%x mu emu com raw %{ @@ -49,11 +49,16 @@ ID [^\s!"#%-,\.\/;->@\[-\^`\{-~]+/{LOOKAHEAD} return 'CONTENT'; } +"{{{{/"[^\s!"#%-,\.\/;->@\[-\^`\{-~]+/[=}\s\/.]"}}}}" { yytext = yytext.substr(5, yyleng-9); this.popState(); return 'END_RAW_BLOCK'; } +[^\x00]*?/("{{{{/") { return 'CONTENT'; } + [\s\S]*?"--}}" strip(0,4); this.popState(); return 'COMMENT'; "(" return 'OPEN_SEXPR'; ")" return 'CLOSE_SEXPR'; +"{{{{" { return 'OPEN_RAW_BLOCK'; } +"}}}}" { this.popState(); this.begin('raw'); return 'CLOSE_RAW_BLOCK'; } "{{{{"[^\x00]*"}}}}" { yytext = yytext.substr(4, yyleng-8); this.popState(); diff --git a/src/handlebars.yy b/src/handlebars.yy index 8b5b77455..464d1bab0 100644 --- a/src/handlebars.yy +++ b/src/handlebars.yy @@ -35,7 +35,7 @@ statements ; statement - : RAW_BLOCK { $$ = new yy.ContentNode($1, @$); } + : openRawBlock CONTENT END_RAW_BLOCK -> new yy.RawBlockNode($1, $2, @$) | openInverse program closeBlock -> new yy.BlockNode($1, $2.inverse, $2, $3, @$) | openBlock program closeBlock -> new yy.BlockNode($1, $2, $2.inverse, $3, @$) | mustache -> $1 @@ -44,6 +44,10 @@ statement | COMMENT -> new yy.CommentNode($1, @$) ; +openRawBlock + : OPEN_RAW_BLOCK sexpr CLOSE_RAW_BLOCK -> new yy.MustacheNode($2, null, '', '', @$) + ; + openBlock : OPEN_BLOCK sexpr CLOSE -> new yy.MustacheNode($2, null, $1, stripFlags($1, $3), @$) ; From 14b7ef9066d107dc83deedc8e6791947811cc764 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Sun, 9 Feb 2014 15:11:12 -0600 Subject: [PATCH 3/3] Make raw blocks operate like blocks --- lib/handlebars/compiler/ast.js | 11 ++++++++--- spec/helpers.js | 8 ++++---- spec/parser.js | 4 ++++ src/handlebars.l | 12 ++++++++++-- src/handlebars.yy | 2 +- 5 files changed, 27 insertions(+), 10 deletions(-) diff --git a/lib/handlebars/compiler/ast.js b/lib/handlebars/compiler/ast.js index b9e67d7fe..286917dbe 100644 --- a/lib/handlebars/compiler/ast.js +++ b/lib/handlebars/compiler/ast.js @@ -131,13 +131,18 @@ var AST = { } }, - RawBlockNode: function(mustache, content, locInfo) { + RawBlockNode: function(mustache, content, close, locInfo) { LocationInfo.call(this, locInfo); + if (mustache.sexpr.id.original !== close) { + throw new Exception(mustache.sexpr.id.original + " doesn't match " + close, this); + } + + content = new AST.ContentNode(content, locInfo); + this.type = 'block'; this.mustache = mustache; - mustache.sexpr.isHelper = mustache.isHelper = true; - mustache.params.splice(0, 0, new AST.StringNode(content, locInfo)); + this.program = new AST.ProgramNode([content], locInfo); }, ContentNode: function(string, locInfo) { diff --git a/spec/helpers.js b/spec/helpers.js index b1d972291..ccde982ba 100644 --- a/spec/helpers.js +++ b/spec/helpers.js @@ -12,8 +12,8 @@ describe('helpers', function() { it("helper for raw block gets raw content", function() { var string = "{{{{raw}}}} {{test}} {{{{/raw}}}}"; var hash = { test: "hello" }; - var helpers = { raw: function(content) { - return content; + var helpers = { raw: function(options) { + return options.fn(); } }; shouldCompileTo(string, [hash, helpers], " {{test}} ", "raw block helper gets raw content"); @@ -22,8 +22,8 @@ describe('helpers', function() { it("helper for raw block gets parameters", function() { var string = "{{{{raw 1 2 3}}}} {{test}} {{{{/raw}}}}"; var hash = { test: "hello" }; - var helpers = { raw: function(content, a, b, c) { - return content + a + b + c; + var helpers = { raw: function(a, b, c, options) { + return options.fn() + a + b + c; } }; shouldCompileTo(string, [hash, helpers], " {{test}} 123", "raw block helper gets raw content"); diff --git a/spec/parser.js b/spec/parser.js index 5b6dc8a4e..097bb15b7 100644 --- a/spec/parser.js +++ b/spec/parser.js @@ -157,6 +157,10 @@ describe('parser', function() { shouldThrow(function() { ast_for("{{#goodbyes}}{{/hellos}}"); }, Error, /goodbyes doesn't match hellos/); + + shouldThrow(function() { + ast_for("{{{{goodbyes}}}} {{{{/hellos}}}}"); + }, Error, /goodbyes doesn't match hellos/); }); it('knows how to report the correct line number in errors', function() { diff --git a/src/handlebars.l b/src/handlebars.l index 9b1266526..cafdd72c7 100644 --- a/src/handlebars.l +++ b/src/handlebars.l @@ -49,7 +49,11 @@ ID [^\s!"#%-,\.\/;->@\[-\^`\{-~]+/{LOOKAHEAD} return 'CONTENT'; } -"{{{{/"[^\s!"#%-,\.\/;->@\[-\^`\{-~]+/[=}\s\/.]"}}}}" { yytext = yytext.substr(5, yyleng-9); this.popState(); return 'END_RAW_BLOCK'; } +"{{{{/"[^\s!"#%-,\.\/;->@\[-\^`\{-~]+/[=}\s\/.]"}}}}" { + yytext = yytext.substr(5, yyleng-9); + this.popState(); + return 'END_RAW_BLOCK'; + } [^\x00]*?/("{{{{/") { return 'CONTENT'; } [\s\S]*?"--}}" strip(0,4); this.popState(); return 'COMMENT'; @@ -58,7 +62,11 @@ ID [^\s!"#%-,\.\/;->@\[-\^`\{-~]+/{LOOKAHEAD} ")" return 'CLOSE_SEXPR'; "{{{{" { return 'OPEN_RAW_BLOCK'; } -"}}}}" { this.popState(); this.begin('raw'); return 'CLOSE_RAW_BLOCK'; } +"}}}}" { + this.popState(); + this.begin('raw'); + return 'CLOSE_RAW_BLOCK'; + } "{{{{"[^\x00]*"}}}}" { yytext = yytext.substr(4, yyleng-8); this.popState(); diff --git a/src/handlebars.yy b/src/handlebars.yy index 464d1bab0..51796ec6d 100644 --- a/src/handlebars.yy +++ b/src/handlebars.yy @@ -35,7 +35,7 @@ statements ; statement - : openRawBlock CONTENT END_RAW_BLOCK -> new yy.RawBlockNode($1, $2, @$) + : openRawBlock CONTENT END_RAW_BLOCK -> new yy.RawBlockNode($1, $2, $3, @$) | openInverse program closeBlock -> new yy.BlockNode($1, $2.inverse, $2, $3, @$) | openBlock program closeBlock -> new yy.BlockNode($1, $2, $2.inverse, $3, @$) | mustache -> $1