928ba56b95
Rather than keeping state in the AST, which requires some gymnastics, we create a separate visitor flow which does the top down iteration necessary to calculate all of the state needed for proper whitespace control evaluation.
293 lines
9.6 KiB
JavaScript
293 lines
9.6 KiB
JavaScript
/*global Handlebars, handlebarsEnv, shouldThrow */
|
|
describe('ast', function() {
|
|
if (!Handlebars.AST) {
|
|
return;
|
|
}
|
|
|
|
var LOCATION_INFO = {
|
|
start: {
|
|
line: 1,
|
|
column: 1
|
|
},
|
|
end: {
|
|
line: 1,
|
|
column: 1
|
|
}
|
|
};
|
|
|
|
function testLocationInfoStorage(node){
|
|
equals(node.loc.start.line, 1);
|
|
equals(node.loc.start.column, 1);
|
|
equals(node.loc.end.line, 1);
|
|
equals(node.loc.end.column, 1);
|
|
}
|
|
|
|
describe('MustacheStatement', function() {
|
|
it('should store args', function() {
|
|
var id = {isSimple: true},
|
|
hash = {},
|
|
mustache = new handlebarsEnv.AST.MustacheStatement({}, true, {}, LOCATION_INFO);
|
|
equals(mustache.type, 'MustacheStatement');
|
|
equals(mustache.escaped, true);
|
|
testLocationInfoStorage(mustache);
|
|
});
|
|
});
|
|
describe('BlockStatement', function() {
|
|
it('should throw on mustache mismatch', function() {
|
|
shouldThrow(function() {
|
|
handlebarsEnv.parse("\n {{#foo}}{{/bar}}");
|
|
}, Handlebars.Exception, "foo doesn't match bar - 2:5");
|
|
});
|
|
|
|
it('stores location info', function(){
|
|
var sexprNode = new handlebarsEnv.AST.SubExpression([{ original: 'foo'}], null);
|
|
var mustacheNode = new handlebarsEnv.AST.MustacheStatement(sexprNode, false, {});
|
|
var block = new handlebarsEnv.AST.BlockStatement(
|
|
mustacheNode,
|
|
{body: []},
|
|
{body: []},
|
|
{},
|
|
{},
|
|
{},
|
|
LOCATION_INFO);
|
|
testLocationInfoStorage(block);
|
|
});
|
|
});
|
|
describe('PathExpression', function() {
|
|
it('stores location info', function(){
|
|
var idNode = new handlebarsEnv.AST.PathExpression(false, 0, [], 'foo', LOCATION_INFO);
|
|
testLocationInfoStorage(idNode);
|
|
});
|
|
});
|
|
|
|
describe('Hash', function(){
|
|
it('stores location info', function(){
|
|
var hash = new handlebarsEnv.AST.Hash([], LOCATION_INFO);
|
|
testLocationInfoStorage(hash);
|
|
});
|
|
});
|
|
|
|
describe('ContentStatement', function(){
|
|
it('stores location info', function(){
|
|
var content = new handlebarsEnv.AST.ContentStatement("HI", LOCATION_INFO);
|
|
testLocationInfoStorage(content);
|
|
});
|
|
});
|
|
|
|
describe('CommentStatement', function(){
|
|
it('stores location info', function(){
|
|
var comment = new handlebarsEnv.AST.CommentStatement("HI", {}, LOCATION_INFO);
|
|
testLocationInfoStorage(comment);
|
|
});
|
|
});
|
|
|
|
describe('NumberLiteral', function(){
|
|
it('stores location info', function(){
|
|
var integer = new handlebarsEnv.AST.NumberLiteral("6", LOCATION_INFO);
|
|
testLocationInfoStorage(integer);
|
|
});
|
|
});
|
|
|
|
describe('StringLiteral', function(){
|
|
it('stores location info', function(){
|
|
var string = new handlebarsEnv.AST.StringLiteral("6", LOCATION_INFO);
|
|
testLocationInfoStorage(string);
|
|
});
|
|
});
|
|
|
|
describe('BooleanLiteral', function(){
|
|
it('stores location info', function(){
|
|
var bool = new handlebarsEnv.AST.BooleanLiteral("true", LOCATION_INFO);
|
|
testLocationInfoStorage(bool);
|
|
});
|
|
});
|
|
|
|
describe('PartialStatement', function(){
|
|
it('stores location info', function(){
|
|
var pn = new handlebarsEnv.AST.PartialStatement('so_partial', {}, LOCATION_INFO);
|
|
testLocationInfoStorage(pn);
|
|
});
|
|
});
|
|
|
|
describe('Program', function(){
|
|
it('storing location info', function(){
|
|
var pn = new handlebarsEnv.AST.Program([], null, {}, LOCATION_INFO);
|
|
testLocationInfoStorage(pn);
|
|
});
|
|
});
|
|
|
|
describe("Line Numbers", function(){
|
|
var ast, body;
|
|
|
|
function testColumns(node, firstLine, lastLine, firstColumn, lastColumn){
|
|
equals(node.loc.start.line, firstLine);
|
|
equals(node.loc.start.column, firstColumn);
|
|
equals(node.loc.end.line, lastLine);
|
|
equals(node.loc.end.column, lastColumn);
|
|
}
|
|
|
|
ast = Handlebars.parse("line 1 {{line1Token}}\n line 2 {{line2token}}\n line 3 {{#blockHelperOnLine3}}\nline 4{{line4token}}\n" +
|
|
"line5{{else}}\n{{line6Token}}\n{{/blockHelperOnLine3}}");
|
|
body = ast.body;
|
|
|
|
it('gets ContentNode line numbers', function(){
|
|
var contentNode = body[0];
|
|
testColumns(contentNode, 1, 1, 0, 7);
|
|
});
|
|
|
|
it('gets MustacheStatement line numbers', function(){
|
|
var mustacheNode = body[1];
|
|
testColumns(mustacheNode, 1, 1, 7, 21);
|
|
});
|
|
|
|
it('gets line numbers correct when newlines appear', function(){
|
|
testColumns(body[2], 1, 2, 21, 8);
|
|
});
|
|
|
|
it('gets MustacheStatement line numbers correct across newlines', function(){
|
|
var secondMustacheStatement = body[3];
|
|
testColumns(secondMustacheStatement, 2, 2, 8, 22);
|
|
});
|
|
|
|
it('gets the block helper information correct', function(){
|
|
var blockHelperNode = body[5];
|
|
testColumns(blockHelperNode, 3, 7, 8, 23);
|
|
});
|
|
|
|
it('correctly records the line numbers the program of a block helper', function(){
|
|
var blockHelperNode = body[5],
|
|
program = blockHelperNode.program;
|
|
|
|
testColumns(program, 3, 5, 8, 5);
|
|
});
|
|
|
|
it('correctly records the line numbers of an inverse of a block helper', function(){
|
|
var blockHelperNode = body[5],
|
|
inverse = blockHelperNode.inverse;
|
|
|
|
testColumns(inverse, 5, 7, 5, 0);
|
|
});
|
|
});
|
|
|
|
describe('standalone flags', function(){
|
|
describe('mustache', function() {
|
|
it('does not mark mustaches as standalone', function() {
|
|
var ast = Handlebars.parse(' {{comment}} ');
|
|
equals(!!ast.body[0].value, true);
|
|
equals(!!ast.body[2].value, true);
|
|
});
|
|
});
|
|
describe('blocks', function() {
|
|
it('marks block mustaches as standalone', function() {
|
|
var ast = Handlebars.parse(' {{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} '),
|
|
block = ast.body[1];
|
|
|
|
equals(ast.body[0].value, '');
|
|
|
|
equals(block.program.body[0].value, 'foo\n');
|
|
equals(block.inverse.body[0].value, ' bar \n');
|
|
|
|
equals(ast.body[2].value, '');
|
|
});
|
|
it('marks initial block mustaches as standalone', function() {
|
|
var ast = Handlebars.parse('{{# comment}} \nfoo\n {{/comment}}'),
|
|
block = ast.body[0];
|
|
|
|
equals(block.program.body[0].value, 'foo\n');
|
|
});
|
|
it('marks mustaches with children as standalone', function() {
|
|
var ast = Handlebars.parse('{{# comment}} \n{{foo}}\n {{/comment}}'),
|
|
block = ast.body[0];
|
|
|
|
equals(block.program.body[0].value, '');
|
|
equals(block.program.body[1].sexpr.path.original, 'foo');
|
|
equals(block.program.body[2].value, '\n');
|
|
});
|
|
it('marks nested block mustaches as standalone', function() {
|
|
var ast = Handlebars.parse('{{#foo}} \n{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} \n{{/foo}}'),
|
|
body = ast.body[0].program.body,
|
|
block = body[1];
|
|
|
|
equals(body[0].value, '');
|
|
|
|
equals(block.program.body[0].value, 'foo\n');
|
|
equals(block.inverse.body[0].value, ' bar \n');
|
|
|
|
equals(body[0].value, '');
|
|
});
|
|
it('does not mark nested block mustaches as standalone', function() {
|
|
var ast = Handlebars.parse('{{#foo}} {{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} {{/foo}}'),
|
|
body = ast.body[0].program.body,
|
|
block = body[1];
|
|
|
|
equals(body[0].omit, undefined);
|
|
|
|
equals(block.program.body[0].value, ' \nfoo\n');
|
|
equals(block.inverse.body[0].value, ' bar \n ');
|
|
|
|
equals(body[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}}'),
|
|
body = ast.body[0].program.body,
|
|
block = body[0];
|
|
|
|
equals(block.program.body[0].value, ' \nfoo\n');
|
|
equals(block.inverse.body[0].value, ' bar \n ');
|
|
|
|
equals(body[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.body[1];
|
|
|
|
equals(ast.body[0].omit, undefined);
|
|
|
|
equals(block.program.body[0].value, 'foo\n');
|
|
equals(block.inverse.body[0].value, ' bar \n');
|
|
|
|
equals(ast.body[2].value, '');
|
|
});
|
|
});
|
|
describe('partials', function() {
|
|
it('marks partial as standalone', function() {
|
|
var ast = Handlebars.parse('{{> partial }} ');
|
|
equals(ast.body[1].value, '');
|
|
});
|
|
it('marks indented partial as standalone', function() {
|
|
var ast = Handlebars.parse(' {{> partial }} ');
|
|
equals(ast.body[0].value, '');
|
|
equals(ast.body[1].indent, ' ');
|
|
equals(ast.body[2].value, '');
|
|
});
|
|
it('marks those around content as not standalone', function() {
|
|
var ast = Handlebars.parse('a{{> partial }}');
|
|
equals(ast.body[0].omit, undefined);
|
|
|
|
ast = Handlebars.parse('{{> partial }}a');
|
|
equals(ast.body[1].omit, undefined);
|
|
});
|
|
});
|
|
describe('comments', function() {
|
|
it('marks comment as standalone', function() {
|
|
var ast = Handlebars.parse('{{! comment }} ');
|
|
equals(ast.body[1].value, '');
|
|
});
|
|
it('marks indented comment as standalone', function() {
|
|
var ast = Handlebars.parse(' {{! comment }} ');
|
|
equals(ast.body[0].value, '');
|
|
equals(ast.body[2].value, '');
|
|
});
|
|
it('marks those around content as not standalone', function() {
|
|
var ast = Handlebars.parse('a{{! comment }}');
|
|
equals(ast.body[0].omit, undefined);
|
|
|
|
ast = Handlebars.parse('{{! comment }}a');
|
|
equals(ast.body[1].omit, undefined);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|