Merge pull request #787 from wycats/standalone-mustaches
Remove whitespace surrounding standalone statements
This commit is contained in:
@@ -9,12 +9,12 @@ function LocationInfo(locInfo){
|
||||
}
|
||||
|
||||
var AST = {
|
||||
ProgramNode: function(statements, inverseStrip, inverse, locInfo) {
|
||||
ProgramNode: function(isRoot, statements, inverseStrip, inverse, locInfo) {
|
||||
var inverseLocationInfo, firstInverseNode;
|
||||
if (arguments.length === 3) {
|
||||
if (arguments.length === 4) {
|
||||
locInfo = inverse;
|
||||
inverse = null;
|
||||
} else if (arguments.length === 2) {
|
||||
} else if (arguments.length === 3) {
|
||||
locInfo = inverseStrip;
|
||||
inverseStrip = null;
|
||||
}
|
||||
@@ -33,14 +33,17 @@ var AST = {
|
||||
last_column: firstInverseNode.lastColumn,
|
||||
first_column: firstInverseNode.firstColumn
|
||||
};
|
||||
this.inverse = new AST.ProgramNode(inverse, inverseStrip, inverseLocationInfo);
|
||||
this.inverse = new AST.ProgramNode(isRoot, inverse, inverseStrip, inverseLocationInfo);
|
||||
} else {
|
||||
this.inverse = new AST.ProgramNode(inverse, inverseStrip);
|
||||
this.inverse = new AST.ProgramNode(isRoot, inverse, inverseStrip);
|
||||
}
|
||||
this.strip.right = inverseStrip.left;
|
||||
} else if (inverseStrip) {
|
||||
this.strip.left = inverseStrip.right;
|
||||
}
|
||||
|
||||
// Scan all children to complete the standalone analysis
|
||||
checkStandalone(this, isRoot, statements);
|
||||
},
|
||||
|
||||
MustacheNode: function(rawParams, hash, open, strip, locInfo) {
|
||||
@@ -104,6 +107,8 @@ var AST = {
|
||||
this.context = context;
|
||||
this.hash = hash;
|
||||
this.strip = strip;
|
||||
|
||||
this.strip.inlineStandalone = true;
|
||||
},
|
||||
|
||||
BlockNode: function(mustache, program, inverse, close, locInfo) {
|
||||
@@ -118,13 +123,31 @@ var AST = {
|
||||
this.program = program;
|
||||
this.inverse = inverse;
|
||||
|
||||
var firstChild = program || inverse,
|
||||
lastChild = inverse || program;
|
||||
|
||||
this.strip = {
|
||||
left: mustache.strip.left,
|
||||
right: close.strip.right
|
||||
right: close.strip.right,
|
||||
|
||||
// Determine the standalone candiacy. Basically flag our content as being possibly standalone
|
||||
// so our parent can determine if we actually are standalone
|
||||
openStandalone: isNextWhitespace(firstChild),
|
||||
closeStandalone: isPrevWhitespace(lastChild)
|
||||
};
|
||||
|
||||
(program || inverse).strip.left = mustache.strip.right;
|
||||
(inverse || program).strip.right = close.strip.left;
|
||||
// Calculate stripping for any else statements
|
||||
firstChild.strip.left = mustache.strip.right;
|
||||
lastChild.strip.right = close.strip.left;
|
||||
|
||||
// Find standalone else statments
|
||||
if (program && inverse
|
||||
&& isPrevWhitespace(program)
|
||||
&& isNextWhitespace(inverse)) {
|
||||
|
||||
omitLeft(program);
|
||||
omitRight(inverse);
|
||||
}
|
||||
|
||||
if (inverse && !program) {
|
||||
this.isInverse = true;
|
||||
@@ -142,7 +165,7 @@ var AST = {
|
||||
|
||||
this.type = 'block';
|
||||
this.mustache = mustache;
|
||||
this.program = new AST.ProgramNode([content], locInfo);
|
||||
this.program = new AST.ProgramNode(false, [content], locInfo);
|
||||
},
|
||||
|
||||
ContentNode: function(string, locInfo) {
|
||||
@@ -238,9 +261,121 @@ var AST = {
|
||||
LocationInfo.call(this, locInfo);
|
||||
this.type = "comment";
|
||||
this.comment = comment;
|
||||
|
||||
this.strip = {
|
||||
inlineStandalone: true
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function checkStandalone(program, isRoot, statements) {
|
||||
for (var i = 0, l = statements.length; i < l; i++) {
|
||||
var current = statements[i],
|
||||
strip = current.strip;
|
||||
|
||||
if (!strip) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var _isPrevWhitespace = isPrevWhitespace(program, i, isRoot, current.type === 'partial'),
|
||||
_isNextWhitespace = isNextWhitespace(program, i, isRoot);
|
||||
strip.openStandalone = strip.openStandalone && _isPrevWhitespace;
|
||||
strip.closeStandalone = strip.closeStandalone && _isNextWhitespace;
|
||||
strip.inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace;
|
||||
|
||||
if (strip.inlineStandalone) {
|
||||
omitRight(program, i);
|
||||
|
||||
if (omitLeft(program, i)) {
|
||||
// If we are on a standalone node, save the indent info for partials
|
||||
if (current.type === 'partial') {
|
||||
current.indent = statements[i-1].string;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (strip.openStandalone) {
|
||||
omitRight(current.program || current.inverse);
|
||||
|
||||
// Strip out the previous content node if it's whitespace only
|
||||
omitLeft(program, i);
|
||||
}
|
||||
if (strip.closeStandalone) {
|
||||
// Always strip the next node
|
||||
omitRight(program, i);
|
||||
|
||||
omitLeft(current.inverse || current.program);
|
||||
}
|
||||
}
|
||||
}
|
||||
function isPrevWhitespace(parent, i, isRoot, disallowIndent) {
|
||||
var statements = parent.statements;
|
||||
if (i === undefined) {
|
||||
i = statements.length;
|
||||
}
|
||||
|
||||
// Nodes that end with newlines are considered whitespace (but are special
|
||||
// cased for strip operations)
|
||||
var prev = statements[i-1];
|
||||
if (prev && /\n$/.test(prev.string)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return checkWhitespace(isRoot, prev, statements[i-2]);
|
||||
}
|
||||
function isNextWhitespace(parent, i, isRoot) {
|
||||
var statements = parent.statements;
|
||||
if (i === undefined) {
|
||||
i = -1;
|
||||
}
|
||||
|
||||
return checkWhitespace(isRoot, statements[i+1], statements[i+2]);
|
||||
}
|
||||
function checkWhitespace(isRoot, next1, next2, disallowIndent) {
|
||||
if (!next1) {
|
||||
return isRoot;
|
||||
} else if (next1.type === 'content') {
|
||||
// Check if the previous node is empty or whitespace only
|
||||
if (disallowIndent ? !next1.string : /^[\s]*$/.test(next1.string)) {
|
||||
if (next2) {
|
||||
return next2.type === 'content' || /\n$/.test(next1.string);
|
||||
} else {
|
||||
return isRoot || (next1.string.indexOf('\n') >= 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Marks the node to the right of the position as omitted.
|
||||
// I.e. " "{{foo}} will mark the " " node as omitted.
|
||||
//
|
||||
// If i is undefined, then the first child will be marked as such.
|
||||
function omitRight(program, i) {
|
||||
var first = program.statements[i == null ? 0 : i + 1];
|
||||
if (first) {
|
||||
first.omit = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Marks the node to the left of the position as omitted.
|
||||
// I.e. " "{{foo}} will mark the " " node as omitted.
|
||||
//
|
||||
// If i is undefined then the last child will be marked as such.
|
||||
function omitLeft(program, i) {
|
||||
var statements = program.statements;
|
||||
if (i === undefined) {
|
||||
i = statements.length;
|
||||
}
|
||||
|
||||
var last = statements[i-1],
|
||||
prev = statements[i-2];
|
||||
|
||||
// We omit the last node if it's whitespace only and not preceeded by a non-content node.
|
||||
if (last && /^[\s]*$/.test(last.string) && (!prev || prev.type === 'content')) {
|
||||
return last.omit = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Must be exported as an object rather than the root of the module as the jison lexer
|
||||
// most modify the object to operate properly.
|
||||
export default AST;
|
||||
|
||||
@@ -217,12 +217,14 @@ Compiler.prototype = {
|
||||
this.opcode('push', 'depth0');
|
||||
}
|
||||
|
||||
this.opcode('invokePartial', partialName.name);
|
||||
this.opcode('invokePartial', partialName.name, partial.indent || '');
|
||||
this.opcode('append');
|
||||
},
|
||||
|
||||
content: function(content) {
|
||||
this.opcode('appendContent', content.string);
|
||||
if (!content.omit) {
|
||||
this.opcode('appendContent', content.string);
|
||||
}
|
||||
},
|
||||
|
||||
mustache: function(mustache) {
|
||||
|
||||
@@ -596,8 +596,8 @@ JavaScriptCompiler.prototype = {
|
||||
//
|
||||
// This operation pops off a context, invokes a partial with that context,
|
||||
// and pushes the result of the invocation back.
|
||||
invokePartial: function(name) {
|
||||
var params = [this.nameLookup('partials', name, 'partial'), "'" + name + "'", this.popStack(), this.popStack(), "helpers", "partials"];
|
||||
invokePartial: function(name, indent) {
|
||||
var params = [this.nameLookup('partials', name, 'partial'), "'" + indent + "'", "'" + name + "'", this.popStack(), this.popStack(), "helpers", "partials"];
|
||||
|
||||
if (this.options.data) {
|
||||
params.push("data");
|
||||
|
||||
@@ -31,18 +31,31 @@ export function template(templateSpec, env) {
|
||||
// for external users to override these as psuedo-supported APIs.
|
||||
env.VM.checkRevision(templateSpec.compiler);
|
||||
|
||||
var invokePartialWrapper = function(partial, name, context, hash, helpers, partials, data) {
|
||||
var invokePartialWrapper = function(partial, indent, name, context, hash, helpers, partials, data) {
|
||||
if (hash) {
|
||||
context = Utils.extend({}, context, hash);
|
||||
}
|
||||
|
||||
var result = env.VM.invokePartial.call(this, partial, name, context, helpers, partials, data);
|
||||
if (result != null) { return result; }
|
||||
|
||||
if (env.compile) {
|
||||
if (result == null && env.compile) {
|
||||
var options = { helpers: helpers, partials: partials, data: data };
|
||||
partials[name] = env.compile(partial, { data: data !== undefined }, env);
|
||||
return partials[name](context, options);
|
||||
result = partials[name](context, options);
|
||||
}
|
||||
if (result != null) {
|
||||
if (indent) {
|
||||
var lines = result.split('\n');
|
||||
for (var i = 0, l = lines.length; i < l; i++) {
|
||||
if (!lines[i] && i + 1 === l) {
|
||||
break;
|
||||
}
|
||||
|
||||
lines[i] = indent + lines[i];
|
||||
}
|
||||
result = lines.join('\n');
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
throw new Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
|
||||
}
|
||||
|
||||
+157
-9
@@ -86,7 +86,7 @@ describe('ast', function() {
|
||||
var sexprNode = new handlebarsEnv.AST.SexprNode([{ original: 'foo'}], null);
|
||||
var mustacheNode = new handlebarsEnv.AST.MustacheNode(sexprNode, null, '{{', {});
|
||||
var block = new handlebarsEnv.AST.BlockNode(mustacheNode,
|
||||
{strip: {}}, {strip: {}},
|
||||
{statements: [], strip: {}}, {statements: [], strip: {}},
|
||||
{
|
||||
strip: {},
|
||||
path: {original: 'foo'}
|
||||
@@ -201,12 +201,12 @@ describe('ast', function() {
|
||||
|
||||
describe("storing location info", function(){
|
||||
it("stores when `inverse` argument isn't passed", function(){
|
||||
var pn = new handlebarsEnv.AST.ProgramNode([], LOCATION_INFO);
|
||||
var pn = new handlebarsEnv.AST.ProgramNode(false, [], LOCATION_INFO);
|
||||
testLocationInfoStorage(pn);
|
||||
});
|
||||
|
||||
it("stores when `inverse` or `stripInverse` arguments passed", function(){
|
||||
var pn = new handlebarsEnv.AST.ProgramNode([], {strip: {}}, undefined, LOCATION_INFO);
|
||||
var pn = new handlebarsEnv.AST.ProgramNode(false, [], {strip: {}}, undefined, LOCATION_INFO);
|
||||
testLocationInfoStorage(pn);
|
||||
|
||||
var clone = {
|
||||
@@ -216,7 +216,7 @@ describe('ast', function() {
|
||||
firstColumn: 0,
|
||||
lastColumn: 0
|
||||
};
|
||||
pn = new handlebarsEnv.AST.ProgramNode([], {strip: {}}, [ clone ], LOCATION_INFO);
|
||||
pn = new handlebarsEnv.AST.ProgramNode(false, [], {strip: {}}, [ clone ], LOCATION_INFO);
|
||||
testLocationInfoStorage(pn);
|
||||
|
||||
// Assert that the newly created ProgramNode has the same location
|
||||
@@ -251,26 +251,174 @@ describe('ast', function() {
|
||||
});
|
||||
|
||||
it('gets line numbers correct when newlines appear', function(){
|
||||
var secondContentNode = statements[2];
|
||||
testColumns(secondContentNode, 1, 2, 21, 8);
|
||||
testColumns(statements[2], 1, 2, 21, 0);
|
||||
testColumns(statements[3], 2, 2, 0, 8);
|
||||
});
|
||||
|
||||
it('gets MustacheNode line numbers correct across newlines', function(){
|
||||
var secondMustacheNode = statements[3];
|
||||
var secondMustacheNode = statements[4];
|
||||
testColumns(secondMustacheNode, 2, 2, 8, 22);
|
||||
});
|
||||
|
||||
it('gets the block helper information correct', function(){
|
||||
var blockHelperNode = statements[5];
|
||||
var blockHelperNode = statements[7];
|
||||
testColumns(blockHelperNode, 3, 7, 8, 23);
|
||||
});
|
||||
|
||||
it('correctly records the line numbers of an inverse of a block helper', function(){
|
||||
var blockHelperNode = statements[5],
|
||||
var blockHelperNode = statements[7],
|
||||
inverse = blockHelperNode.inverse;
|
||||
|
||||
testColumns(inverse, 5, 6, 13, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('standalone flags', function(){
|
||||
describe('mustache', function() {
|
||||
it('does not mark mustaches as standalone', function() {
|
||||
var ast = Handlebars.parse(' {{comment}} ');
|
||||
equals(ast.statements[0].omit, undefined);
|
||||
equals(ast.statements[2].omit, undefined);
|
||||
});
|
||||
});
|
||||
describe('blocks', function() {
|
||||
it('marks block mustaches as standalone', function() {
|
||||
var ast = Handlebars.parse(' {{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} '),
|
||||
block = ast.statements[1];
|
||||
|
||||
equals(ast.statements[0].omit, true);
|
||||
|
||||
equals(block.program.statements[0].omit, true);
|
||||
equals(block.program.statements[1].string, 'foo\n');
|
||||
equals(block.program.statements[2].omit, true);
|
||||
|
||||
equals(block.inverse.statements[0].omit, true);
|
||||
equals(block.inverse.statements[1].string, ' bar \n');
|
||||
equals(block.inverse.statements[2].omit, true);
|
||||
|
||||
equals(ast.statements[2].omit, true);
|
||||
});
|
||||
it('marks initial block mustaches as standalone', function() {
|
||||
var ast = Handlebars.parse('{{# comment}} \nfoo\n {{/comment}}'),
|
||||
block = ast.statements[0];
|
||||
|
||||
equals(block.program.statements[0].omit, true);
|
||||
equals(block.program.statements[1].string, 'foo\n');
|
||||
equals(block.program.statements[2].omit, true);
|
||||
});
|
||||
it('marks mustaches with children as standalone', function() {
|
||||
var ast = Handlebars.parse('{{# comment}} \n{{foo}}\n {{/comment}}'),
|
||||
block = ast.statements[0];
|
||||
|
||||
equals(block.program.statements[0].omit, true);
|
||||
equals(block.program.statements[1].id.original, 'foo');
|
||||
equals(block.program.statements[2].omit, undefined);
|
||||
equals(block.program.statements[3].omit, true);
|
||||
});
|
||||
it('marks nested block mustaches as standalone', function() {
|
||||
var ast = Handlebars.parse('{{#foo}} \n{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} \n{{/foo}}'),
|
||||
statements = ast.statements[0].program.statements,
|
||||
block = statements[1];
|
||||
|
||||
equals(statements[0].omit, true);
|
||||
|
||||
equals(block.program.statements[0].omit, true);
|
||||
equals(block.program.statements[1].string, 'foo\n');
|
||||
equals(block.program.statements[2].omit, true);
|
||||
|
||||
equals(block.inverse.statements[0].omit, true);
|
||||
equals(block.inverse.statements[1].string, ' bar \n');
|
||||
equals(block.inverse.statements[2].omit, true);
|
||||
|
||||
equals(statements[0].omit, true);
|
||||
});
|
||||
it('does not mark nested block mustaches as standalone', function() {
|
||||
var ast = Handlebars.parse('{{#foo}} {{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} {{/foo}}'),
|
||||
statements = ast.statements[0].program.statements,
|
||||
block = statements[1];
|
||||
|
||||
equals(statements[0].omit, undefined);
|
||||
|
||||
equals(block.program.statements[0].omit, undefined);
|
||||
equals(block.program.statements[1].string, 'foo\n');
|
||||
equals(block.program.statements[2].omit, true);
|
||||
|
||||
equals(block.inverse.statements[0].omit, true);
|
||||
equals(block.inverse.statements[1].string, ' bar \n');
|
||||
equals(block.inverse.statements[2].omit, undefined);
|
||||
|
||||
equals(statements[0].omit, undefined);
|
||||
});
|
||||
it('does not mark nested initial block mustaches as standalone', function() {
|
||||
var ast = Handlebars.parse('{{#foo}}{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}}{{/foo}}'),
|
||||
statements = ast.statements[0].program.statements,
|
||||
block = statements[0];
|
||||
|
||||
equals(block.program.statements[0].omit, undefined);
|
||||
equals(block.program.statements[1].string, 'foo\n');
|
||||
equals(block.program.statements[2].omit, true);
|
||||
|
||||
equals(block.inverse.statements[0].omit, true);
|
||||
equals(block.inverse.statements[1].string, ' bar \n');
|
||||
equals(block.inverse.statements[2].omit, undefined);
|
||||
|
||||
equals(statements[0].omit, undefined);
|
||||
});
|
||||
|
||||
it('marks column 0 block mustaches as standalone', function() {
|
||||
var ast = Handlebars.parse('test\n{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} '),
|
||||
block = ast.statements[1];
|
||||
|
||||
equals(ast.statements[0].omit, undefined);
|
||||
|
||||
equals(block.program.statements[0].omit, true);
|
||||
equals(block.program.statements[1].string, 'foo\n');
|
||||
equals(block.program.statements[2].omit, true);
|
||||
|
||||
equals(block.inverse.statements[0].omit, true);
|
||||
equals(block.inverse.statements[1].string, ' bar \n');
|
||||
equals(block.inverse.statements[2].omit, true);
|
||||
|
||||
equals(ast.statements[2].omit, true);
|
||||
});
|
||||
});
|
||||
describe('partials', function() {
|
||||
it('marks partial as standalone', function() {
|
||||
var ast = Handlebars.parse('{{> partial }} ');
|
||||
equals(ast.statements[1].omit, true);
|
||||
});
|
||||
it('marks indented partial as standalone', function() {
|
||||
var ast = Handlebars.parse(' {{> partial }} ');
|
||||
equals(ast.statements[0].omit, true);
|
||||
equals(ast.statements[1].indent, ' ');
|
||||
equals(ast.statements[2].omit, true);
|
||||
});
|
||||
it('marks those around content as not standalone', function() {
|
||||
var ast = Handlebars.parse('a{{> partial }}');
|
||||
equals(ast.statements[0].omit, undefined);
|
||||
|
||||
ast = Handlebars.parse('{{> partial }}a');
|
||||
equals(ast.statements[1].omit, undefined);
|
||||
});
|
||||
});
|
||||
describe('comments', function() {
|
||||
it('marks comment as standalone', function() {
|
||||
var ast = Handlebars.parse('{{! comment }} ');
|
||||
equals(ast.statements[1].omit, true);
|
||||
});
|
||||
it('marks indented comment as standalone', function() {
|
||||
var ast = Handlebars.parse(' {{! comment }} ');
|
||||
equals(ast.statements[0].omit, true);
|
||||
equals(ast.statements[2].omit, true);
|
||||
});
|
||||
it('marks those around content as not standalone', function() {
|
||||
var ast = Handlebars.parse('a{{! comment }}');
|
||||
equals(ast.statements[0].omit, undefined);
|
||||
|
||||
ast = Handlebars.parse('{{! comment }}a');
|
||||
equals(ast.statements[1].omit, undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -83,4 +83,15 @@ describe('blocks', function() {
|
||||
"No people");
|
||||
});
|
||||
});
|
||||
|
||||
describe('standalone sections', function() {
|
||||
it('block standalone else sections', function() {
|
||||
shouldCompileTo('{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n', {none: 'No people'},
|
||||
'No people\n');
|
||||
shouldCompileTo('{{#none}}\n{{.}}\n{{^}}\n{{none}}\n{{/none}}\n', {none: 'No people'},
|
||||
'No people\n');
|
||||
shouldCompileTo('\n{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n', {none: 'No people'},
|
||||
'No people\n');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+1
-1
@@ -184,7 +184,7 @@ describe('parser', function() {
|
||||
|
||||
describe('externally compiled AST', function() {
|
||||
it('can pass through an already-compiled AST', function() {
|
||||
equals(ast_for(new Handlebars.AST.ProgramNode([ new Handlebars.AST.ContentNode("Hello")])), "CONTENT[ \'Hello\' ]\n");
|
||||
equals(ast_for(new Handlebars.AST.ProgramNode(false, [ new Handlebars.AST.ContentNode("Hello")])), "CONTENT[ \'Hello\' ]\n");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+19
-1
@@ -1,4 +1,4 @@
|
||||
/*global CompilerContext, shouldCompileTo, shouldCompileToWithPartials */
|
||||
/*global CompilerContext, Handlebars, handlebarsEnv, shouldCompileTo, shouldCompileToWithPartials, shouldThrow */
|
||||
describe('partials', function() {
|
||||
it("basic partials", function() {
|
||||
var string = "Dudes: {{#dudes}}{{> dude}}{{/dudes}}";
|
||||
@@ -137,4 +137,22 @@ describe('partials', function() {
|
||||
var partial = "";
|
||||
var hash = {dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]};
|
||||
shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, "Dudes: "); });
|
||||
|
||||
describe('standalone partials', function() {
|
||||
it("indented partials", function() {
|
||||
var string = "Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}";
|
||||
var dude = "{{name}}\n";
|
||||
var hash = {dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]};
|
||||
shouldCompileToWithPartials(string, [hash, {}, {dude: dude}], true,
|
||||
"Dudes:\n Yehuda\n Alan\n");
|
||||
});
|
||||
it("nested indented partials", function() {
|
||||
var string = "Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}";
|
||||
var dude = "{{name}}\n {{> url}}";
|
||||
var url = "{{url}}!\n";
|
||||
var hash = {dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]};
|
||||
shouldCompileToWithPartials(string, [hash, {}, {dude: dude, url: url}], true,
|
||||
"Dudes:\n Yehuda\n http://yehuda!\n Alan\n http://alan!\n");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+21
-3
@@ -24,7 +24,19 @@ describe('Regressions', function() {
|
||||
});
|
||||
|
||||
it("bug reported by @fat where lambdas weren't being properly resolved", function() {
|
||||
var string = "<strong>This is a slightly more complicated {{thing}}.</strong>.\n{{! Just ignore this business. }}\nCheck this out:\n{{#hasThings}}\n<ul>\n{{#things}}\n<li class={{className}}>{{word}}</li>\n{{/things}}</ul>.\n{{/hasThings}}\n{{^hasThings}}\n\n<small>Nothing to check out...</small>\n{{/hasThings}}";
|
||||
var string = '<strong>This is a slightly more complicated {{thing}}.</strong>.\n'
|
||||
+ '{{! Just ignore this business. }}\n'
|
||||
+ 'Check this out:\n'
|
||||
+ '{{#hasThings}}\n'
|
||||
+ '<ul>\n'
|
||||
+ '{{#things}}\n'
|
||||
+ '<li class={{className}}>{{word}}</li>\n'
|
||||
+ '{{/things}}</ul>.\n'
|
||||
+ '{{/hasThings}}\n'
|
||||
+ '{{^hasThings}}\n'
|
||||
+ '\n'
|
||||
+ '<small>Nothing to check out...</small>\n'
|
||||
+ '{{/hasThings}}';
|
||||
var data = {
|
||||
thing: function() {
|
||||
return "blah";
|
||||
@@ -39,7 +51,13 @@ describe('Regressions', function() {
|
||||
}
|
||||
};
|
||||
|
||||
var output = "<strong>This is a slightly more complicated blah.</strong>.\n\nCheck this out:\n\n<ul>\n\n<li class=one>@fat</li>\n\n<li class=two>@dhg</li>\n\n<li class=three>@sayrer</li>\n</ul>.\n\n";
|
||||
var output = '<strong>This is a slightly more complicated blah.</strong>.\n'
|
||||
+ 'Check this out:\n'
|
||||
+ '<ul>\n'
|
||||
+ '<li class=one>@fat</li>\n'
|
||||
+ '<li class=two>@dhg</li>\n'
|
||||
+ '<li class=three>@sayrer</li>\n'
|
||||
+ '</ul>.\n';
|
||||
shouldCompileTo(string, data, output);
|
||||
});
|
||||
|
||||
@@ -128,7 +146,7 @@ describe('Regressions', function() {
|
||||
|
||||
if (Handlebars.AST) {
|
||||
it("can pass through an already-compiled AST via compile/precompile", function() {
|
||||
equal(Handlebars.compile(new Handlebars.AST.ProgramNode([ new Handlebars.AST.ContentNode("Hello")]))(), 'Hello');
|
||||
equal(Handlebars.compile(new Handlebars.AST.ProgramNode(true, [ new Handlebars.AST.ContentNode("Hello")]))(), 'Hello');
|
||||
});
|
||||
|
||||
it("can pass through an empty string", function() {
|
||||
|
||||
+2
-2
@@ -28,7 +28,7 @@ ID [^\s!"#%-,\.\/;->@\[-\^`\{-~]+/{LOOKAHEAD}
|
||||
|
||||
%%
|
||||
|
||||
[^\x00]*?/("{{") {
|
||||
[^\x00\n]*?\n?/("{{") {
|
||||
if(yytext.slice(-2) === "\\\\") {
|
||||
strip(0,1);
|
||||
this.begin("mu");
|
||||
@@ -41,7 +41,7 @@ ID [^\s!"#%-,\.\/;->@\[-\^`\{-~]+/{LOOKAHEAD}
|
||||
if(yytext) return 'CONTENT';
|
||||
}
|
||||
|
||||
[^\x00]+ return 'CONTENT';
|
||||
([^\x00\n]+\n?|\n) return 'CONTENT';
|
||||
|
||||
// marks CONTENT up to the next mustache or escaped mustache
|
||||
<emu>[^\x00]{2,}?/("{{"|"\\{{"|"\\\\{{"|<<EOF>>) {
|
||||
|
||||
+8
-8
@@ -16,17 +16,17 @@ function stripFlags(open, close) {
|
||||
%%
|
||||
|
||||
root
|
||||
: statements EOF { return new yy.ProgramNode($1, @$); }
|
||||
| EOF { return new yy.ProgramNode([], @$); }
|
||||
: statements EOF { return new yy.ProgramNode(true, $1, @$); }
|
||||
| EOF { return new yy.ProgramNode(true, [], @$); }
|
||||
;
|
||||
|
||||
program
|
||||
: simpleInverse statements -> new yy.ProgramNode([], $1, $2, @$)
|
||||
| statements simpleInverse statements -> new yy.ProgramNode($1, $2, $3, @$)
|
||||
| statements simpleInverse -> new yy.ProgramNode($1, $2, [], @$)
|
||||
| statements -> new yy.ProgramNode($1, @$)
|
||||
| simpleInverse -> new yy.ProgramNode([], @$)
|
||||
| "" -> new yy.ProgramNode([], @$)
|
||||
: simpleInverse statements -> new yy.ProgramNode(false, [], $1, $2, @$)
|
||||
| statements simpleInverse statements -> new yy.ProgramNode(false, $1, $2, $3, @$)
|
||||
| statements simpleInverse -> new yy.ProgramNode(false, $1, $2, [], @$)
|
||||
| statements -> new yy.ProgramNode(false, $1, @$)
|
||||
| simpleInverse -> new yy.ProgramNode(false, [], @$)
|
||||
| "" -> new yy.ProgramNode(false, [], @$)
|
||||
;
|
||||
|
||||
statements
|
||||
|
||||
Reference in New Issue
Block a user