diff --git a/spec/basic.js b/spec/basic.js index e0e9a6ab..67b4300d 100644 --- a/spec/basic.js +++ b/spec/basic.js @@ -11,7 +11,9 @@ describe("basic context", function() { it("escaping", function() { shouldCompileTo("\\{{foo}}", { foo: "food" }, "{{foo}}"); + shouldCompileTo("content \\{{foo}}", { foo: "food" }, "content {{foo}}"); shouldCompileTo("\\\\{{foo}}", { foo: "food" }, "\\food"); + shouldCompileTo("content \\\\{{foo}}", { foo: "food" }, "content \\food"); shouldCompileTo("\\\\ {{foo}}", { foo: "food" }, "\\\\ food"); }); diff --git a/spec/tokenizer.js b/spec/tokenizer.js index f86774a6..de981e42 100644 --- a/spec/tokenizer.js +++ b/spec/tokenizer.js @@ -58,6 +58,7 @@ describe('Tokenizer', function() { var result = tokenize("{{foo}} \\{{bar}} {{baz}}"); result.should.match_tokens(['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); + result[3].should.be_token("CONTENT", " "); result[4].should.be_token("CONTENT", "{{bar}} "); }); @@ -77,6 +78,43 @@ describe('Tokenizer', function() { result[4].should.be_token("CONTENT", "{{{bar}}} "); }); + it('supports escaping escape character', function() { + var result = tokenize("{{foo}} \\\\{{bar}} {{baz}}"); + result.should.match_tokens(['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); + + result[3].should.be_token("CONTENT", " \\"); + result[5].should.be_token("ID", "bar"); + }); + + it('supports escaping multiple escape characters', function() { + var result = tokenize("{{foo}} \\\\{{bar}} \\\\{{baz}}"); + result.should.match_tokens(['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); + + result[3].should.be_token("CONTENT", " \\"); + result[5].should.be_token("ID", "bar"); + result[7].should.be_token("CONTENT", " \\"); + result[9].should.be_token("ID", "baz"); + }); + + it('supports mixed escaped delimiters and escaped escape characters', function() { + var result = tokenize("{{foo}} \\\\{{bar}} \\{{baz}}"); + result.should.match_tokens(['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'CONTENT']); + + result[3].should.be_token("CONTENT", " \\"); + result[4].should.be_token("OPEN", "{{"); + result[5].should.be_token("ID", "bar"); + result[7].should.be_token("CONTENT", " "); + result[8].should.be_token("CONTENT", "{{baz}}"); + }); + + it('supports escaped escape character on a triple stash', function() { + var result = tokenize("{{foo}} \\\\{{{bar}}} {{baz}}"); + result.should.match_tokens(['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN_UNESCAPED', 'ID', 'CLOSE_UNESCAPED', 'CONTENT', 'OPEN', 'ID', 'CLOSE']); + + result[3].should.be_token("CONTENT", " \\"); + result[5].should.be_token("ID", "bar"); + }); + it('tokenizes a simple path', function() { var result = tokenize("{{foo/bar}}"); result.should.match_tokens(['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']); diff --git a/src/handlebars.l b/src/handlebars.l index 018096b0..7593189e 100644 --- a/src/handlebars.l +++ b/src/handlebars.l @@ -23,10 +23,16 @@ ID [^\s!"#%-,\.\/;->@\[-\^`\{-~]+/[=}\s\/.] %% -"\\\\"/("{{") yytext = "\\"; return 'CONTENT'; [^\x00]*?/("{{") { - if(yytext.slice(-1) !== "\\") this.begin("mu"); - if(yytext.slice(-1) === "\\") strip(0,1), this.begin("emu"); + if(yytext.slice(-2) === "\\\\") { + strip(0,1); + this.begin("mu"); + } else if(yytext.slice(-1) === "\\") { + strip(0,1); + this.begin("emu"); + } else { + this.begin("mu"); + } if(yytext) return 'CONTENT'; }