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:
kpdecker
2014-08-23 17:44:20 -05:00
parent ccd803ff15
commit 84d646bb5d
4 changed files with 81 additions and 53 deletions
+1 -13
View File
@@ -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) {
+66 -18
View File
@@ -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, ...
+14
View File
@@ -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');
});
});