New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement parser for else chaining of helpers #892

Merged
merged 1 commit into from Nov 8, 2014
Jump to file or symbol
Failed to load files and symbols.
+72 −11
Diff settings

Always

Just for now

@@ -10,11 +10,29 @@ export function stripFlags(open, close) {
export function prepareBlock(mustache, program, inverseAndProgram, close, inverted, locInfo) {
/*jshint -W040 */
if (mustache.sexpr.id.original !== close.path.original) {
// When we are chaining inverse calls, we will not have a close path
if (close && close.path && (mustache.sexpr.id.original !== close.path.original)) {
throw new Exception(mustache.sexpr.id.original + ' doesn\'t match ' + close.path.original, mustache);
}
var inverse = inverseAndProgram && inverseAndProgram.program;
// Safely handle a chained inverse that does not have a non-conditional inverse
// (i.e. both inverseAndProgram AND close are undefined)
if (!close) {
close = {strip: {}};
}
// Find the inverse program that is involed with whitespace stripping.
var inverse = inverseAndProgram && inverseAndProgram.program,
firstInverse = inverse,
lastInverse = inverse;
if (inverse && inverse.inverse) {
firstInverse = inverse.statements[0].program;
// Walk the inverse chain to find the last inverse that is actually in the chain.
while (lastInverse.inverse) {
lastInverse = lastInverse.statements[lastInverse.statements.length-1].program;
}
}
var strip = {
left: mustache.strip.left,
@@ -23,7 +41,7 @@ export function prepareBlock(mustache, program, inverseAndProgram, close, invert
// Determine the standalone candiacy. Basically flag our content as being possibly standalone
// so our parent can determine if we actually are standalone
openStandalone: isNextWhitespace(program.statements),
closeStandalone: isPrevWhitespace((inverse || program).statements)
closeStandalone: isPrevWhitespace((firstInverse || program).statements)
};
if (mustache.strip.right) {
@@ -36,19 +54,20 @@ export function prepareBlock(mustache, program, inverseAndProgram, close, invert
if (inverseStrip.left) {
omitLeft(program.statements, null, true);
}
if (inverseStrip.right) {
omitRight(inverse.statements, null, true);
omitRight(firstInverse.statements, null, true);
}
if (close.strip.left) {
omitLeft(inverse.statements, null, true);
omitLeft(lastInverse.statements, null, true);
}
// Find standalone else statments
if (isPrevWhitespace(program.statements)
&& isNextWhitespace(inverse.statements)) {
&& isNextWhitespace(firstInverse.statements)) {
omitLeft(program.statements);
omitRight(inverse.statements);
omitRight(firstInverse.statements);
}
} else {
if (close.strip.left) {
Copy path View file
@@ -89,6 +89,20 @@ describe('blocks', function() {
shouldCompileTo("{{#people}}{{name}}{{^}}{{none}}{{/people}}", {none: "No people"},
"No people");
});
it("chained inverted sections", function() {
shouldCompileTo("{{#people}}{{name}}{{else if none}}{{none}}{{/people}}", {none: "No people"},
"No people");
shouldCompileTo("{{#people}}{{name}}{{else if nothere}}fail{{else unless nothere}}{{none}}{{/people}}", {none: "No people"},
"No people");
shouldCompileTo("{{#people}}{{name}}{{else if none}}{{none}}{{else}}fail{{/people}}", {none: "No people"},
"No people");
});
it("chained inverted sections with mismatch", function() {
shouldThrow(function() {
shouldCompileTo("{{#people}}{{name}}{{else if none}}{{none}}{{/if}}", {none: "No people"},
"No people");
}, Error);
});
it("block inverted sections with empty arrays", function() {
shouldCompileTo("{{#people}}{{name}}{{^}}{{none}}{{/people}}", {none: "No people", people: []},
@@ -105,6 +119,12 @@ describe('blocks', function() {
shouldCompileTo('{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n', {none: 'No people'},
'No people\n');
});
it('block standalone chained else sections', function() {
shouldCompileTo('{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{/people}}\n', {none: 'No people'},
'No people\n');
shouldCompileTo('{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{^}}\n{{/people}}\n', {none: 'No people'},
'No people\n');
});
it('should handle nesting', function() {
shouldCompileTo('{{#data}}\n{{#if true}}\n{{.}}\n{{/if}}\n{{/data}}\nOK.', {data: [1, 3, 5]}, '1\n3\n5\nOK.');
});
Copy path View file
@@ -116,6 +116,10 @@ describe('parser', function() {
equals(ast_for("{{#foo}} bar {{else}} baz {{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n");
});
it('parses multiple inverse sections', function() {
equals(ast_for("{{#foo}} bar {{else if bar}}{{else}} baz {{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n BLOCK:\n {{ ID:if [ID:bar] }}\n PROGRAM:\n {{^}}\n CONTENT[ ' baz ' ]\n");
});
it('parses empty blocks', function() {
equals(ast_for("{{#foo}}{{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n");
});
@@ -147,8 +151,10 @@ describe('parser', function() {
it('parses a standalone inverse section', function() {
equals(ast_for("{{^foo}}bar{{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n {{^}}\n CONTENT[ 'bar' ]\n");
});
it('parses a standalone inverse section', function() {
equals(ast_for("{{else foo}}bar{{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n {{^}}\n CONTENT[ 'bar' ]\n");
it('throws on old inverse section', function() {
shouldThrow(function() {
equals(ast_for("{{else foo}}bar{{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n {{^}}\n CONTENT[ 'bar' ]\n");
}, Error);
});
it("raises if there's a Parse error", function() {
Copy path View file
@@ -73,7 +73,7 @@ ID [^\s!"#%-,\.\/;->@\[-\^`\{-~]+/{LOOKAHEAD}
<mu>"{{"{LEFT_STRIP}?"^"\s*{RIGHT_STRIP}?"}}" this.popState(); return 'INVERSE';
<mu>"{{"{LEFT_STRIP}?\s*"else"\s*{RIGHT_STRIP}?"}}" this.popState(); return 'INVERSE';
<mu>"{{"{LEFT_STRIP}?"^" return 'OPEN_INVERSE';
<mu>"{{"{LEFT_STRIP}?\s*"else" return 'OPEN_INVERSE';
<mu>"{{"{LEFT_STRIP}?\s*"else" return 'OPEN_INVERSE_CHAIN';
<mu>"{{"{LEFT_STRIP}?"{" return 'OPEN_UNESCAPED';
<mu>"{{"{LEFT_STRIP}?"&" return 'OPEN';
<mu>"{{!--" this.popState(); this.begin('com');
Copy path View file
@@ -30,7 +30,7 @@ openRawBlock
;
block
: openBlock program inverseAndProgram? closeBlock -> yy.prepareBlock($1, $2, $3, $4, false, @$)
: openBlock program inverseChain? closeBlock -> yy.prepareBlock($1, $2, $3, $4, false, @$)
| openInverse program inverseAndProgram? closeBlock -> yy.prepareBlock($1, $2, $3, $4, true, @$)
;
@@ -42,10 +42,26 @@ openInverse
: OPEN_INVERSE sexpr CLOSE -> new yy.MustacheNode($2, null, $1, yy.stripFlags($1, $3), @$)
;
openInverseChain
: OPEN_INVERSE_CHAIN sexpr CLOSE -> new yy.MustacheNode($2, null, $1, yy.stripFlags($1, $3), @$)
;
inverseAndProgram
: INVERSE program -> { strip: yy.stripFlags($1, $1), program: $2 }
;
inverseChain
: openInverseChain program inverseChain? {
var inverse = yy.prepareBlock($1, $2, $3, $3, false, @$),
program = new yy.ProgramNode(yy.prepareProgram([inverse]), {}, @$);
program.inverse = inverse;
$$ = { strip: $1.strip, program: program, chain: true };
}
| inverseAndProgram -> $1
;
closeBlock
: OPEN_ENDBLOCK path CLOSE -> {path: $2, strip: yy.stripFlags($1, $3)}
;
ProTip! Use n and p to navigate between commits in a pull request.