diff options
-rw-r--r-- | lib/handlebars/compiler/helpers.js | 88 | ||||
-rw-r--r-- | spec/ast.js | 63 | ||||
-rw-r--r-- | spec/blocks.js | 5 | ||||
-rw-r--r-- | src/handlebars.l | 4 |
4 files changed, 54 insertions, 106 deletions
diff --git a/lib/handlebars/compiler/helpers.js b/lib/handlebars/compiler/helpers.js index dd32299..758c740 100644 --- a/lib/handlebars/compiler/helpers.js +++ b/lib/handlebars/compiler/helpers.js @@ -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; } diff --git a/spec/ast.js b/spec/ast.js index 20988eb..c28e876 100644 --- a/spec/ast.js +++ b/spec/ast.js @@ -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, ''); }); diff --git a/spec/blocks.js b/spec/blocks.js index 9a5cb40..a172970 100644 --- a/spec/blocks.js +++ b/spec/blocks.js @@ -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() { diff --git a/src/handlebars.l b/src/handlebars.l index f775cc4..0f420e7 100644 --- a/src/handlebars.l +++ b/src/handlebars.l @@ -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>>) { |