Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions lib/whitespace-control.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ WhitespaceControl.prototype.Program = function (program) {
// If we are on a standalone node, save the indent info for partials
if (current.type === 'PartialStatement') {
// Pull out the whitespace from the final line
current.indent = /([ \t]+$)/.exec(body[i - 1].original)[1];
current.indent = /(?:^|[^ \t])([ \t]+)$/.exec(
body[i - 1].original
)[1];
}
}
}
Expand Down Expand Up @@ -172,9 +174,13 @@ function isPrevWhitespace(body, i, isRoot) {
}

if (prev.type === 'ContentStatement') {
return (sibling || !isRoot ? /\r?\n\s*?$/ : /(^|\r?\n)\s*?$/).test(
prev.original
);
// Matches when the trailing whitespace contains a newline (or, for the
// root's first node, is the whole string). See omitLeft for the anchoring.
return (
sibling || !isRoot
? /(?:^|\S)[^\S\n]*\n\s*$/
: /^\s*$|(?:^|\S)[^\S\n]*\n\s*$/
).test(prev.original);
}
}
function isNextWhitespace(body, i, isRoot) {
Expand Down Expand Up @@ -239,7 +245,13 @@ function omitLeft(body, i, multiple) {

// We omit the last node if it's whitespace only and not preceded by a non-content node.
let original = current.value;
current.value = current.value.replace(multiple ? /\s+$/ : /[ \t]+$/, '');
// The match must start after a non-whitespace character (or at the start):
// a plain /\s+$/ is retried at every position of a whitespace run, which
// takes quadratic time on long runs.
current.value = current.value.replace(
multiple ? /(^|\S)\s+$/ : /(^|[^ \t])[ \t]+$/,
'$1'
);
current.leftStripped = current.value !== original;
return current.leftStripped;
}
Expand Down
29 changes: 29 additions & 0 deletions spec/ast.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,35 @@ describe('ast', function () {
equals(ast.body[1].program.body[0].value, ' \nfoo\n ');
});
});

describe('performance', function () {
// Each template below takes several seconds to parse, and so exceeds the
// test timeout, if finding trailing whitespace is not linear in its length.
let spaces = ' '.repeat(100000),
newlines = '\n'.repeat(100000);

it('should strip whitespace in linear time', function () {
let ast = parse(spaces + 'x' + spaces + '{{~foo}}');

equals(ast.body[0].value, spaces + 'x');
});

it('should detect standalone lines in linear time', function () {
let ast = parse(newlines + 'x{{foo}}');
equals(ast.body[0].value, newlines + 'x');

ast = parse('{{foo}}' + newlines + 'x{{foo}}');
equals(ast.body[1].value, newlines + 'x');
});

it('should find partial indentation in linear time', function () {
let ast = parse(spaces + 'x\n {{> dude}}\n');

equals(ast.body[0].value, spaces + 'x\n');
equals(ast.body[1].indent, ' ');
equals(ast.body[2].value, '');
});
});
});

describe('node details', function () {
Expand Down
Loading