Skip to content

Commit

Permalink
match: store bitmask instead of index
Browse files Browse the repository at this point in the history
Make possible to jump to the higher priority template with
`applyNext()`.

See: https://github.com/bem/bem-components/pull/1495/files#r30004513
  • Loading branch information
indutny committed May 10, 2015
1 parent 04666ca commit ee7d1de
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 44 deletions.
48 changes: 31 additions & 17 deletions lib/bemhtml/runtime/match.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ function Match(entity) {
this.bemhtml = this.entity.bemhtml;
this.templates = [];

// applyNext index
this.index = 0;
// applyNext mask
this.mask = [ 0 ];

this.count = 0;
}
Expand All @@ -87,33 +87,47 @@ exports.Match = Match;
Match.prototype.push = function push(template) {
this.templates.push(new MatchTemplate(this, template));
this.count++;

if (Math.ceil(this.count / 31) > this.mask.length)
this.mask.push(0);
};

Match.prototype.exec = function exec(context) {
// Fast case - no templates
if (this.count === this.index)
return undefined;

var template;
for (var i = this.index; i < this.count; i++) {
template = this.templates[i];
for (var j = template.predicates.length - 1; j >= 0; j--) {
var pred = template.predicates[j];
if (!pred.exec(context))
var mask = this.mask[0];
var bitIndex = 0;
var bit = 1;
for (var i = 0; i < this.count; i++) {
if ((mask & bit) === 0) {
template = this.templates[i];
for (var j = template.predicates.length - 1; j >= 0; j--) {
var pred = template.predicates[j];

/* jshint maxdepth : false */
if (!pred.exec(context))
break;
}

// All predicates matched!
if (j === -1)
break;
}

// All predicates matched!
if (j === -1)
break;
if (bit === 0x40000000) {
bitIndex++;
mask = this.mask[bitIndex];
bit = 1;
} else {
bit <<= 1;
}
}

if (i === this.count)
return undefined;

var oldIndex = this.index;
var oldMask = mask;
var oldMatch = this.bemhtml.match;
this.index = i + 1;
this.mask[bitIndex] |= bit;
this.bemhtml.match = this;

var out;
Expand All @@ -122,7 +136,7 @@ Match.prototype.exec = function exec(context) {
else
out = template.body;

this.index = oldIndex;
this.mask[bitIndex] = oldMask;
this.bemhtml.match = oldMatch;

return out;
Expand Down
81 changes: 54 additions & 27 deletions test/runtime-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,63 @@ var fixtures = require('./fixtures');
var test = fixtures.test;

describe('BEMHTML compiler/Runtime', function() {
it('should support applyNext()', function() {
test(function() {
block('b1').content()(function() {
return '%' + applyNext() + '%';
});
block('b1').content()(function() {
return '{' + applyNext() + '}';
});
}, { block: 'b1', content: 'ohai' }, '<div class="b1">{%ohai%}</div>');
});
describe('applyNext()', function() {
it('should support applyNext()', function() {
test(function() {
block('b1').content()(function() {
return '%' + applyNext() + '%';
});
block('b1').content()(function() {
return '{' + applyNext() + '}';
});
}, { block: 'b1', content: 'ohai' }, '<div class="b1">{%ohai%}</div>');
});

it('should support applyNext({ ... })', function() {
test(function() {
block('b1').content()(function() {
return '%' + this.wtf + applyNext() + '%';
});
block('b1').content()(function() {
return '{' + applyNext({ wtf: 'no ' }) + '}';
});
}, { block: 'b1', content: 'ohai' }, '<div class="b1">{%no ohai%}</div>');
});
it('should support applyNext({ ... })', function() {
test(function() {
block('b1').content()(function() {
return '%' + this.wtf + applyNext() + '%';
});
block('b1').content()(function() {
return '{' + applyNext({ wtf: 'no ' }) + '}';
});
}, { block: 'b1', content: 'ohai' }, '<div class="b1">{%no ohai%}</div>');
});

it('should support local', function() {
test(function() {
block('b1').content()(function() {
return local({ tmp: 'b2' })(function() {
return this.tmp;
it('should support local', function() {
test(function() {
block('b1').content()(function() {
return local({ tmp: 'b2' })(function() {
return this.tmp;
});
});
});
}, { block: 'b1' }, '<div class="b1">b2</div>');
}, { block: 'b1' }, '<div class="b1">b2</div>');
});

it('should support visiting higher priority templates', function() {
test(function() {
block('b1').content()(function() {
return applyNext({ wtf: true });
});

block('b1').match(function() {
return this.wtf;
}).content()('ok');
}, { block: 'b1' }, '<div class="b1">ok</div>');
});

it('should support > 31 templates (because of the bit mask)', function() {
test(function() {
block('b1').content()(function() {
return 'ok';
});
for (var i = 0; i < 128; i++) {
block('b1').content()(function() {
return applyNext();
});
}
}, { block: 'b1' }, '<div class="b1">ok</div>');
});
});

it('should support applyCtx', function() {
Expand Down

0 comments on commit ee7d1de

Please sign in to comment.