Move strip processing into AST helper logic
We already have to track these behaviors for the standalone parsing and rather than having two whitespace pruning implementations this moves all of the behavior into one place. Fixes #852
This commit is contained in:
@@ -69,19 +69,7 @@ Compiler.prototype = {
|
||||
},
|
||||
|
||||
accept: function(node) {
|
||||
var strip = node.strip || {},
|
||||
ret;
|
||||
if (strip.left) {
|
||||
this.opcode('strip');
|
||||
}
|
||||
|
||||
ret = this[node.type](node);
|
||||
|
||||
if (strip.right) {
|
||||
this.opcode('strip');
|
||||
}
|
||||
|
||||
return ret;
|
||||
return this[node.type](node);
|
||||
},
|
||||
|
||||
program: function(program) {
|
||||
|
||||
@@ -26,13 +26,22 @@ export function prepareBlock(mustache, program, inverseAndProgram, close, invert
|
||||
closeStandalone: isPrevWhitespace((inverse || program).statements)
|
||||
};
|
||||
|
||||
if (mustache.strip.right) {
|
||||
omitRight(program.statements, null, true);
|
||||
}
|
||||
|
||||
if (inverse) {
|
||||
var inverseStrip = inverseAndProgram.strip;
|
||||
|
||||
program.strip.left = mustache.strip.right;
|
||||
program.strip.right = inverseStrip.left;
|
||||
inverse.strip.left = inverseStrip.right;
|
||||
inverse.strip.right = close.strip.left;
|
||||
if (inverseStrip.left) {
|
||||
omitLeft(program.statements, null, true);
|
||||
}
|
||||
if (inverseStrip.right) {
|
||||
omitRight(inverse.statements, null, true);
|
||||
}
|
||||
if (close.strip.left) {
|
||||
omitLeft(inverse.statements, null, true);
|
||||
}
|
||||
|
||||
// Find standalone else statments
|
||||
if (isPrevWhitespace(program.statements)
|
||||
@@ -42,8 +51,9 @@ export function prepareBlock(mustache, program, inverseAndProgram, close, invert
|
||||
omitRight(inverse.statements);
|
||||
}
|
||||
} else {
|
||||
program.strip.left = mustache.strip.right;
|
||||
program.strip.right = close.strip.left;
|
||||
if (close.strip.left) {
|
||||
omitLeft(program.statements, null, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (inverted) {
|
||||
@@ -70,6 +80,13 @@ export function prepareProgram(statements, isRoot) {
|
||||
closeStandalone = strip.closeStandalone && _isNextWhitespace,
|
||||
inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace;
|
||||
|
||||
if (strip.right) {
|
||||
omitRight(statements, i, true);
|
||||
}
|
||||
if (strip.left) {
|
||||
omitLeft(statements, i, true);
|
||||
}
|
||||
|
||||
if (inlineStandalone) {
|
||||
omitRight(statements, i);
|
||||
|
||||
@@ -137,10 +154,25 @@ function checkWhitespace(isRoot, next1, next2, disallowIndent) {
|
||||
// I.e. {{foo}}' ' will mark the ' ' node as omitted.
|
||||
//
|
||||
// If i is undefined, then the first child will be marked as such.
|
||||
function omitRight(statements, i) {
|
||||
var first = statements[i == null ? 0 : i + 1];
|
||||
if (first) {
|
||||
first.string = '';
|
||||
//
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,17 +180,33 @@ function omitRight(statements, i) {
|
||||
// I.e. ' '{{foo}} will mark the ' ' node as omitted.
|
||||
//
|
||||
// If i is undefined then the last child will be marked as such.
|
||||
function omitLeft(statements, i) {
|
||||
if (i === undefined) {
|
||||
//
|
||||
// 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 last = statements[i-1],
|
||||
prev = statements[i-2];
|
||||
var current = statements[--i],
|
||||
prev = statements[i-1];
|
||||
|
||||
// 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')) {
|
||||
last.string = '';
|
||||
return true;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,11 +85,6 @@ JavaScriptCompiler.prototype = {
|
||||
opcode = opcodes[i];
|
||||
|
||||
this[opcode.opcode].apply(this, opcode.args);
|
||||
|
||||
// Reset the stripNext flag if it was not set by this operation.
|
||||
if (opcode.opcode !== this.stripNext) {
|
||||
this.stripNext = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Flush any trailing content that might be pending.
|
||||
@@ -280,27 +275,10 @@ JavaScriptCompiler.prototype = {
|
||||
if (this.pendingContent) {
|
||||
content = this.pendingContent + content;
|
||||
}
|
||||
if (this.stripNext) {
|
||||
content = content.replace(/^\s+/, '');
|
||||
}
|
||||
|
||||
this.pendingContent = content;
|
||||
},
|
||||
|
||||
// [strip]
|
||||
//
|
||||
// On stack, before: ...
|
||||
// On stack, after: ...
|
||||
//
|
||||
// Removes any trailing whitespace from the prior content node and flags
|
||||
// the next operation for stripping if it is a content node.
|
||||
strip: function() {
|
||||
if (this.pendingContent) {
|
||||
this.pendingContent = this.pendingContent.replace(/\s+$/, '');
|
||||
}
|
||||
this.stripNext = 'strip';
|
||||
},
|
||||
|
||||
// [append]
|
||||
//
|
||||
// On stack, before: value, ...
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/*global shouldCompileTo, shouldCompileToWithPartials */
|
||||
|
||||
describe('whitespace control', function() {
|
||||
it('should strip whitespace around mustache calls', function() {
|
||||
var hash = {foo: 'bar<'};
|
||||
@@ -8,6 +10,8 @@ describe('whitespace control', function() {
|
||||
|
||||
shouldCompileTo(' {{~&foo~}} ', hash, 'bar<');
|
||||
shouldCompileTo(' {{~{foo}~}} ', hash, 'bar<');
|
||||
|
||||
shouldCompileTo('1\n{{foo~}} \n\n 23\n{{bar}}4', {}, '1\n23\n4');
|
||||
});
|
||||
|
||||
describe('blocks', function() {
|
||||
@@ -18,6 +22,9 @@ describe('whitespace control', function() {
|
||||
shouldCompileTo(' {{#if foo~}} bar {{/if~}} ', hash, ' bar ');
|
||||
shouldCompileTo(' {{~#if foo}} bar {{~/if}} ', hash, ' bar ');
|
||||
shouldCompileTo(' {{#if foo}} bar {{/if}} ', hash, ' bar ');
|
||||
|
||||
shouldCompileTo(' \n\n{{~#if foo~}} \n\nbar \n\n{{~/if~}}\n\n ', hash, 'bar');
|
||||
shouldCompileTo(' a\n\n{{~#if foo~}} \n\nbar \n\n{{~/if~}}\n\na ', hash, ' abara ');
|
||||
});
|
||||
it('should strip whitespace around inverse block calls', function() {
|
||||
var hash = {};
|
||||
@@ -26,6 +33,8 @@ describe('whitespace control', function() {
|
||||
shouldCompileTo(' {{^if foo~}} bar {{/if~}} ', hash, ' bar ');
|
||||
shouldCompileTo(' {{~^if foo}} bar {{~/if}} ', hash, ' bar ');
|
||||
shouldCompileTo(' {{^if foo}} bar {{/if}} ', hash, ' bar ');
|
||||
|
||||
shouldCompileTo(' \n\n{{~^if foo~}} \n\nbar \n\n{{~/if~}}\n\n ', hash, 'bar');
|
||||
});
|
||||
it('should strip whitespace around complex block calls', function() {
|
||||
var hash = {foo: 'bar<'};
|
||||
@@ -37,6 +46,9 @@ describe('whitespace control', function() {
|
||||
|
||||
shouldCompileTo('{{#if foo~}} bar {{~else~}} baz {{~/if}}', hash, 'bar');
|
||||
|
||||
shouldCompileTo('\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n', hash, 'bar');
|
||||
shouldCompileTo('\n\n{{~#if foo~}} \n\n{{{foo}}} \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n', hash, 'bar<');
|
||||
|
||||
hash = {};
|
||||
|
||||
shouldCompileTo('{{#if foo~}} bar {{~^~}} baz {{~/if}}', hash, 'baz');
|
||||
@@ -45,6 +57,8 @@ describe('whitespace control', function() {
|
||||
shouldCompileTo('{{#if foo~}} bar {{~^}} baz {{/if}}', hash, ' baz ');
|
||||
|
||||
shouldCompileTo('{{#if foo~}} bar {{~else~}} baz {{~/if}}', hash, 'baz');
|
||||
|
||||
shouldCompileTo('\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n', hash, 'baz');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user