Refactor content blocks to ignore lines
We can simplify our previous standalone determination logic by merging content blocks again. Fixes #854
This commit is contained in:
@@ -93,7 +93,7 @@ export function prepareProgram(statements, isRoot) {
|
||||
if (omitLeft(statements, i)) {
|
||||
// If we are on a standalone node, save the indent info for partials
|
||||
if (current.type === 'partial') {
|
||||
current.indent = statements[i-1].original;
|
||||
current.indent = (/([ \t]+$)/).exec(statements[i-1].original) ? RegExp.$1 : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,39 +114,36 @@ export function prepareProgram(statements, isRoot) {
|
||||
return statements;
|
||||
}
|
||||
|
||||
function isPrevWhitespace(statements, i, isRoot, disallowIndent) {
|
||||
function isPrevWhitespace(statements, i, isRoot) {
|
||||
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;
|
||||
var prev = statements[i-1],
|
||||
sibling = statements[i-2];
|
||||
if (!prev) {
|
||||
return isRoot;
|
||||
}
|
||||
|
||||
return checkWhitespace(isRoot, prev, statements[i-2]);
|
||||
if (prev.type === 'content') {
|
||||
return (sibling || !isRoot ? (/\r?\n\s*?$/) : (/(^|\r?\n)\s*?$/)).test(prev.original);
|
||||
}
|
||||
}
|
||||
function isNextWhitespace(statements, i, isRoot) {
|
||||
if (i === undefined) {
|
||||
i = -1;
|
||||
}
|
||||
|
||||
return checkWhitespace(isRoot, statements[i+1], statements[i+2]);
|
||||
}
|
||||
function checkWhitespace(isRoot, next1, next2) {
|
||||
if (!next1) {
|
||||
var next = statements[i+1],
|
||||
sibling = statements[i+2];
|
||||
if (!next) {
|
||||
return isRoot;
|
||||
} else if (next1.type === 'content') {
|
||||
// Check if the previous node is empty or whitespace only
|
||||
if (/^[\s]*$/.test(next1.string)) {
|
||||
if (next2) {
|
||||
return next2.type === 'content' || /\n$/.test(next1.string);
|
||||
} else {
|
||||
return isRoot || (next1.string.indexOf('\n') >= 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (next.type === 'content') {
|
||||
return (sibling || !isRoot ? (/^\s*?\r?\n/) : (/^\s*?(\r?\n|$)/)).test(next.original);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,22 +155,14 @@ function checkWhitespace(isRoot, next1, next2) {
|
||||
// If mulitple is truthy then all whitespace will be stripped out until non-whitespace
|
||||
// content is met.
|
||||
function omitRight(statements, i, multiple) {
|
||||
i = i == null ? 0 : i + 1;
|
||||
|
||||
var current = statements[i];
|
||||
while (current) {
|
||||
if (current.type !== 'content') {
|
||||
return;
|
||||
}
|
||||
|
||||
current.string = current.string.replace(/^[\s]+/, '');
|
||||
|
||||
if (multiple && !current.string) {
|
||||
current = statements[++i];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
var current = statements[i == null ? 0 : i + 1];
|
||||
if (!current || current.type !== 'content' || (!multiple && current.rightStripped)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var original = current.string;
|
||||
current.string = current.string.replace(multiple ? (/^\s+/) : (/^[ \t]*\r?\n?/), '');
|
||||
current.rightStripped = current.string !== original;
|
||||
}
|
||||
|
||||
// Marks the node to the left of the position as omitted.
|
||||
@@ -184,29 +173,14 @@ function omitRight(statements, i, multiple) {
|
||||
// If mulitple is truthy then all whitespace will be stripped out until non-whitespace
|
||||
// content is met.
|
||||
function omitLeft(statements, i, multiple) {
|
||||
if (i == null) {
|
||||
i = statements.length;
|
||||
var current = statements[i == null ? statements.length - 1 : i - 1];
|
||||
if (!current || current.type !== 'content' || (!multiple && current.leftStripped)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var current = statements[--i],
|
||||
prev = statements[i-1];
|
||||
|
||||
while (current) {
|
||||
if (current.type !== 'content') {
|
||||
return;
|
||||
}
|
||||
|
||||
// We omit the last node if it's whitespace only and not preceeded by a non-content node.
|
||||
if (multiple || (/^[\s]*$/.test(current.string) && (!prev || prev.type === 'content'))) {
|
||||
current.string = current.string.replace(/[\s]+$/, '');
|
||||
|
||||
if (multiple && !current.string) {
|
||||
current = statements[--i];
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// We omit the last node if it's whitespace only and not preceeded by a non-content node.
|
||||
var original = current.string;
|
||||
current.string = current.string.replace(multiple ? (/\s+$/) : (/[ \t]+$/), '');
|
||||
current.leftStripped = current.string !== original;
|
||||
return current.leftStripped;
|
||||
}
|
||||
|
||||
+17
-46
@@ -222,29 +222,28 @@ describe('ast', function() {
|
||||
});
|
||||
|
||||
it('gets line numbers correct when newlines appear', function(){
|
||||
testColumns(statements[2], 1, 2, 21, 0);
|
||||
testColumns(statements[3], 2, 2, 0, 8);
|
||||
testColumns(statements[2], 1, 2, 21, 8);
|
||||
});
|
||||
|
||||
it('gets MustacheNode line numbers correct across newlines', function(){
|
||||
var secondMustacheNode = statements[4];
|
||||
var secondMustacheNode = statements[3];
|
||||
testColumns(secondMustacheNode, 2, 2, 8, 22);
|
||||
});
|
||||
|
||||
it('gets the block helper information correct', function(){
|
||||
var blockHelperNode = statements[7];
|
||||
var blockHelperNode = statements[5];
|
||||
testColumns(blockHelperNode, 3, 7, 8, 23);
|
||||
});
|
||||
|
||||
it('correctly records the line numbers the program of a block helper', function(){
|
||||
var blockHelperNode = statements[7],
|
||||
var blockHelperNode = statements[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 = statements[7],
|
||||
var blockHelperNode = statements[5],
|
||||
inverse = blockHelperNode.inverse;
|
||||
|
||||
testColumns(inverse, 5, 7, 5, 0);
|
||||
@@ -266,13 +265,8 @@ describe('ast', function() {
|
||||
|
||||
equals(ast.statements[0].string, '');
|
||||
|
||||
equals(block.program.statements[0].string, '');
|
||||
equals(block.program.statements[1].string, 'foo\n');
|
||||
equals(block.program.statements[2].string, '');
|
||||
|
||||
equals(block.inverse.statements[0].string, '');
|
||||
equals(block.inverse.statements[1].string, ' bar \n');
|
||||
equals(block.inverse.statements[2].string, '');
|
||||
equals(block.program.statements[0].string, 'foo\n');
|
||||
equals(block.inverse.statements[0].string, ' bar \n');
|
||||
|
||||
equals(ast.statements[2].string, '');
|
||||
});
|
||||
@@ -280,9 +274,7 @@ describe('ast', function() {
|
||||
var ast = Handlebars.parse('{{# comment}} \nfoo\n {{/comment}}'),
|
||||
block = ast.statements[0];
|
||||
|
||||
equals(block.program.statements[0].string, '');
|
||||
equals(block.program.statements[1].string, 'foo\n');
|
||||
equals(block.program.statements[2].string, '');
|
||||
equals(block.program.statements[0].string, 'foo\n');
|
||||
});
|
||||
it('marks mustaches with children as standalone', function() {
|
||||
var ast = Handlebars.parse('{{# comment}} \n{{foo}}\n {{/comment}}'),
|
||||
@@ -290,8 +282,7 @@ describe('ast', function() {
|
||||
|
||||
equals(block.program.statements[0].string, '');
|
||||
equals(block.program.statements[1].id.original, 'foo');
|
||||
equals(block.program.statements[2].omit, undefined);
|
||||
equals(block.program.statements[3].string, '');
|
||||
equals(block.program.statements[2].string, '\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}}'),
|
||||
@@ -300,13 +291,8 @@ describe('ast', function() {
|
||||
|
||||
equals(statements[0].string, '');
|
||||
|
||||
equals(block.program.statements[0].string, '');
|
||||
equals(block.program.statements[1].string, 'foo\n');
|
||||
equals(block.program.statements[2].string, '');
|
||||
|
||||
equals(block.inverse.statements[0].string, '');
|
||||
equals(block.inverse.statements[1].string, ' bar \n');
|
||||
equals(block.inverse.statements[2].string, '');
|
||||
equals(block.program.statements[0].string, 'foo\n');
|
||||
equals(block.inverse.statements[0].string, ' bar \n');
|
||||
|
||||
equals(statements[0].string, '');
|
||||
});
|
||||
@@ -317,13 +303,8 @@ describe('ast', function() {
|
||||
|
||||
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].string, '');
|
||||
|
||||
equals(block.inverse.statements[0].string, '');
|
||||
equals(block.inverse.statements[1].string, ' bar \n');
|
||||
equals(block.inverse.statements[2].omit, undefined);
|
||||
equals(block.program.statements[0].string, ' \nfoo\n');
|
||||
equals(block.inverse.statements[0].string, ' bar \n ');
|
||||
|
||||
equals(statements[0].omit, undefined);
|
||||
});
|
||||
@@ -332,13 +313,8 @@ describe('ast', function() {
|
||||
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].string, '');
|
||||
|
||||
equals(block.inverse.statements[0].string, '');
|
||||
equals(block.inverse.statements[1].string, ' bar \n');
|
||||
equals(block.inverse.statements[2].omit, undefined);
|
||||
equals(block.program.statements[0].string, ' \nfoo\n');
|
||||
equals(block.inverse.statements[0].string, ' bar \n ');
|
||||
|
||||
equals(statements[0].omit, undefined);
|
||||
});
|
||||
@@ -349,13 +325,8 @@ describe('ast', function() {
|
||||
|
||||
equals(ast.statements[0].omit, undefined);
|
||||
|
||||
equals(block.program.statements[0].string, '');
|
||||
equals(block.program.statements[1].string, 'foo\n');
|
||||
equals(block.program.statements[2].string, '');
|
||||
|
||||
equals(block.inverse.statements[0].string, '');
|
||||
equals(block.inverse.statements[1].string, ' bar \n');
|
||||
equals(block.inverse.statements[2].string, '');
|
||||
equals(block.program.statements[0].string, 'foo\n');
|
||||
equals(block.inverse.statements[0].string, ' bar \n');
|
||||
|
||||
equals(ast.statements[2].string, '');
|
||||
});
|
||||
|
||||
+4
-1
@@ -102,9 +102,12 @@ describe('blocks', function() {
|
||||
'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'},
|
||||
shouldCompileTo('{{#people}}\n{{name}}\n{{^}}\n{{none}}\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.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('compat mode', function() {
|
||||
|
||||
+2
-2
@@ -28,7 +28,7 @@ ID [^\s!"#%-,\.\/;->@\[-\^`\{-~]+/{LOOKAHEAD}
|
||||
|
||||
%%
|
||||
|
||||
[^\x00\n]*?\n?/("{{") {
|
||||
[^\x00]*?/("{{") {
|
||||
if(yytext.slice(-2) === "\\\\") {
|
||||
strip(0,1);
|
||||
this.begin("mu");
|
||||
@@ -41,7 +41,7 @@ ID [^\s!"#%-,\.\/;->@\[-\^`\{-~]+/{LOOKAHEAD}
|
||||
if(yytext) return 'CONTENT';
|
||||
}
|
||||
|
||||
([^\x00\n]+\n?|\n) return 'CONTENT';
|
||||
[^\x00]+ return 'CONTENT';
|
||||
|
||||
// marks CONTENT up to the next mustache or escaped mustache
|
||||
<emu>[^\x00]{2,}?/("{{"|"\\{{"|"\\\\{{"|<<EOF>>) {
|
||||
|
||||
Reference in New Issue
Block a user