Browse Source

Isolated rules management & rules cache build

pull/14/head
Vitaly Puzrin 10 years ago
parent
commit
2b741deb18
  1. 11
      lib/linkifier.js
  2. 19
      lib/parser_block.js
  3. 18
      lib/parser_inline.js
  4. 88
      lib/ruler.js
  5. 5
      lib/rules_block/blockquote.js
  6. 3
      lib/rules_block/list.js
  7. 28
      lib/rules_block/paragraph.js
  8. 11
      lib/typographer.js

11
lib/linkifier.js

@ -13,11 +13,9 @@ var _rules = [
function Linkifier() { function Linkifier() {
this._rules = [];
this.options = {}; this.options = {};
this.ruler = new Ruler(this.rulesUpdate.bind(this)); this.ruler = new Ruler();
for (var i = 0; i < _rules.length; i++) { for (var i = 0; i < _rules.length; i++) {
this.ruler.push(_rules[i][0], _rules[i][1]); this.ruler.push(_rules[i][0], _rules[i][1]);
@ -25,11 +23,6 @@ function Linkifier() {
} }
Linkifier.prototype.rulesUpdate = function () {
this._rules = this.ruler.getRules();
};
Linkifier.prototype.set = function (options) { Linkifier.prototype.set = function (options) {
assign(this.options, options); assign(this.options, options);
}; };
@ -38,7 +31,7 @@ Linkifier.prototype.set = function (options) {
Linkifier.prototype.process = function (state) { Linkifier.prototype.process = function (state) {
var i, l, rules; var i, l, rules;
rules = this._rules; rules = this.ruler.getRules('');
for (i = 0, l = rules.length; i < l; i++) { for (i = 0, l = rules.length; i < l; i++) {
rules[i](this, state); rules[i](this, state);

19
lib/parser_block.js

@ -25,12 +25,7 @@ var _rules = [
// Block Parser class // Block Parser class
// //
function ParserBlock() { function ParserBlock() {
this._rules = []; this.ruler = new Ruler();
this._rulesParagraphTerm = [];
this._rulesBlockquoteTerm = [];
this._rulesListTerm = [];
this.ruler = new Ruler(this.rulesUpdate.bind(this));
for (var i = 0; i < _rules.length; i++) { for (var i = 0; i < _rules.length; i++) {
this.ruler.push(_rules[i][0], _rules[i][1], { alt: (_rules[i][2] || []).slice() }); this.ruler.push(_rules[i][0], _rules[i][1], { alt: (_rules[i][2] || []).slice() });
@ -38,20 +33,12 @@ function ParserBlock() {
} }
ParserBlock.prototype.rulesUpdate = function () {
this._rules = this.ruler.getRules();
this._rulesParagraphTerm = this.ruler.getRules('paragraph');
this._rulesBlockquoteTerm = this.ruler.getRules('blockquote');
this._rulesListTerm = this.ruler.getRules('list');
};
// Generate tokens for input range // Generate tokens for input range
// //
ParserBlock.prototype.tokenize = function (state, startLine, endLine) { ParserBlock.prototype.tokenize = function (state, startLine, endLine) {
var ok, i, var ok, i,
rules = this._rules, rules = this.ruler.getRules(''),
len = this._rules.length, len = rules.length,
line = startLine, line = startLine,
hasEmptyLines = false; hasEmptyLines = false;

18
lib/parser_inline.js

@ -47,7 +47,6 @@ function validateLink(url) {
// Inline Parser class // Inline Parser class
// //
function ParserInline() { function ParserInline() {
this._rules = [];
// Rule to skip pure text // Rule to skip pure text
// - '{}$%@+=:' reserved for extentions // - '{}$%@+=:' reserved for extentions
@ -57,7 +56,7 @@ function ParserInline() {
// If you need to restrict it - override this with your validator. // If you need to restrict it - override this with your validator.
this.validateLink = validateLink; this.validateLink = validateLink;
this.ruler = new Ruler(this.rulesUpdate.bind(this)); this.ruler = new Ruler();
for (var i = 0; i < _rules.length; i++) { for (var i = 0; i < _rules.length; i++) {
this.ruler.push(_rules[i][0], _rules[i][1]); this.ruler.push(_rules[i][0], _rules[i][1]);
@ -65,17 +64,13 @@ function ParserInline() {
} }
ParserInline.prototype.rulesUpdate = function () {
this._rules = this.ruler.getRules();
};
// Skip single token by running all rules in validation mode; // Skip single token by running all rules in validation mode;
// returns `true` if any rule reported success // returns `true` if any rule reported success
// //
ParserInline.prototype.skipToken = function (state) { ParserInline.prototype.skipToken = function (state) {
var i, cached_pos, pos = state.pos, var i, cached_pos, pos = state.pos,
len = this._rules.length; rules = this.ruler.getRules(''),
len = rules.length;
if ((cached_pos = state.cacheGet(pos)) > 0) { if ((cached_pos = state.cacheGet(pos)) > 0) {
state.pos = cached_pos; state.pos = cached_pos;
@ -83,7 +78,7 @@ ParserInline.prototype.skipToken = function (state) {
} }
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
if (this._rules[i](state, true)) { if (rules[i](state, true)) {
state.cacheSet(pos, state.pos); state.cacheSet(pos, state.pos);
return; return;
} }
@ -98,7 +93,8 @@ ParserInline.prototype.skipToken = function (state) {
// //
ParserInline.prototype.tokenize = function (state) { ParserInline.prototype.tokenize = function (state) {
var ok, i, var ok, i,
len = this._rules.length, rules = this.ruler.getRules(''),
len = rules.length,
end = state.posMax; end = state.posMax;
while (state.pos < end) { while (state.pos < end) {
@ -111,7 +107,7 @@ ParserInline.prototype.tokenize = function (state) {
// - return true // - return true
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
ok = this._rules[i](state, false); ok = rules[i](state, false);
if (ok) { break; } if (ok) { break; }
} }

88
lib/ruler.js

@ -9,9 +9,7 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
function Ruler(compileFn) { function Ruler() {
this.compile = compileFn; // callback to call after each change
// List of added rules. Each element is: // List of added rules. Each element is:
// //
// { // {
@ -22,6 +20,13 @@ function Ruler(compileFn) {
// } // }
// //
this.rules = []; this.rules = [];
// Cached rule chains.
//
// First level - chain name, '' for default.
// Second level - diginal anchor for fast filtering by charcodes.
//
this.cache = null;
} }
@ -47,7 +52,7 @@ Ruler.prototype.at = function (name, fn, options) {
this.rules[index].fn = fn; this.rules[index].fn = fn;
this.rules[index].alt = opt.alt || []; this.rules[index].alt = opt.alt || [];
this.compile(); this.cache = null;
}; };
@ -66,7 +71,7 @@ Ruler.prototype.before = function (beforeName, ruleName, fn, options) {
alt: opt.alt || [] alt: opt.alt || []
}); });
this.compile(); this.cache = null;
}; };
@ -85,7 +90,7 @@ Ruler.prototype.after = function (afterName, ruleName, fn, options) {
alt: opt.alt || [] alt: opt.alt || []
}); });
this.compile(); this.cache = null;
}; };
// Add rule to the end of chain. // Add rule to the end of chain.
@ -100,30 +105,7 @@ Ruler.prototype.push = function (ruleName, fn, options) {
alt: opt.alt || [] alt: opt.alt || []
}); });
this.compile(); this.cache = null;
};
// Get rules list as array of functions. By default returns main chain
//
Ruler.prototype.getRules = function (chainName) {
var result = [];
if (!chainName) {
this.rules.forEach(function (rule) {
if (rule.enabled) {
result.push(rule.fn);
}
});
return result;
}
this.rules.forEach(function (rule) {
if (rule.alt.indexOf(chainName) >= 0 && rule.enabled) {
result.push(rule.fn);
}
});
return result;
}; };
@ -151,7 +133,7 @@ Ruler.prototype.enable = function (list, strict) {
}, this); }, this);
this.compile(); this.cache = null;
}; };
@ -171,8 +153,50 @@ Ruler.prototype.disable = function (list) {
}, this); }, this);
this.compile(); this.cache = null;
};
// Build rules lookup cache
//
Ruler.prototype.compile = function () {
var self = this;
var chains = [ '' ];
// collect unique names
self.rules.forEach(function (rule) {
if (!rule.enabled) { return; }
rule.alt.forEach(function (altName) {
if (chains.indexOf(altName) < 0) {
chains.push(altName);
}
});
});
self.cache = {};
chains.forEach(function (chain) {
self.cache[chain] = [];
self.rules.forEach(function (rule) {
if (!rule.enabled) { return; }
if (chain && rule.alt.indexOf(chain) < 0) { return; }
self.cache[chain].push(rule.fn);
});
});
}; };
// Get rules list as array of functions.
//
Ruler.prototype.getRules = function (chainName) {
if (this.cache === null) {
this.compile();
}
return this.cache[chainName];
};
module.exports = Ruler; module.exports = Ruler;

5
lib/rules_block/blockquote.js

@ -5,7 +5,8 @@
module.exports = function blockquote(state, startLine, endLine, silent) { module.exports = function blockquote(state, startLine, endLine, silent) {
var nextLine, lastLineEmpty, oldTShift, oldBMarks, oldIndent, oldParentType, lines, var nextLine, lastLineEmpty, oldTShift, oldBMarks, oldIndent, oldParentType, lines,
terminatorRules = state.parser._rulesBlockquoteTerm, i, l, terminate, terminatorRules,
i, l, terminate,
pos = state.bMarks[startLine] + state.tShift[startLine], pos = state.bMarks[startLine] + state.tShift[startLine],
max = state.eMarks[startLine]; max = state.eMarks[startLine];
@ -36,6 +37,8 @@ module.exports = function blockquote(state, startLine, endLine, silent) {
oldTShift = [ state.tShift[startLine] ]; oldTShift = [ state.tShift[startLine] ];
state.tShift[startLine] = pos - state.bMarks[startLine]; state.tShift[startLine] = pos - state.bMarks[startLine];
terminatorRules = state.parser.ruler.getRules('blockquote');
// Search the end of the block // Search the end of the block
// //
// Block ends with either: // Block ends with either:

3
lib/rules_block/list.js

@ -102,7 +102,7 @@ module.exports = function list(state, startLine, endLine, silent) {
listLines, listLines,
itemLines, itemLines,
tight = true, tight = true,
terminatorRules = state.parser._rulesListTerm, terminatorRules,
i, l, terminate; i, l, terminate;
// Detect list type and position after marker // Detect list type and position after marker
@ -150,6 +150,7 @@ module.exports = function list(state, startLine, endLine, silent) {
nextLine = startLine; nextLine = startLine;
prevEmptyEnd = false; prevEmptyEnd = false;
terminatorRules = state.parser.ruler.getRules('list');
while (nextLine < endLine) { while (nextLine < endLine) {
contentStart = state.skipSpaces(posAfterMarker); contentStart = state.skipSpaces(posAfterMarker);

28
lib/rules_block/paragraph.js

@ -9,25 +9,29 @@ var parseRef = require('../parser_ref');
module.exports = function paragraph(state, startLine/*, endLine*/) { module.exports = function paragraph(state, startLine/*, endLine*/) {
var endLine, content, pos, terminate, i, l, var endLine, content, pos, terminate, i, l,
nextLine = startLine + 1, nextLine = startLine + 1,
terminatorRules = state.parser._rulesParagraphTerm; terminatorRules;
endLine = state.lineMax; endLine = state.lineMax;
// jump line-by-line until empty one or EOF // jump line-by-line until empty one or EOF
for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) { if (nextLine < endLine && !state.isEmpty(nextLine)) {
// this would be a code block normally, but after paragraph terminatorRules = state.parser.ruler.getRules('paragraph');
// it's considered a lazy continuation regardless of what's there
if (state.tShift[nextLine] - state.blkIndent > 3) { continue; }
// Some tags can terminate paragraph without empty line. for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {
terminate = false; // this would be a code block normally, but after paragraph
for (i = 0, l = terminatorRules.length; i < l; i++) { // it's considered a lazy continuation regardless of what's there
if (terminatorRules[i](state, nextLine, endLine, true)) { if (state.tShift[nextLine] - state.blkIndent > 3) { continue; }
terminate = true;
break; // Some tags can terminate paragraph without empty line.
terminate = false;
for (i = 0, l = terminatorRules.length; i < l; i++) {
if (terminatorRules[i](state, nextLine, endLine, true)) {
terminate = true;
break;
}
} }
if (terminate) { break; }
} }
if (terminate) { break; }
} }
content = state.getLines(startLine, nextLine, state.blkIndent, false).trim(); content = state.getLines(startLine, nextLine, state.blkIndent, false).trim();

11
lib/typographer.js

@ -18,11 +18,9 @@ var _rules = [
function Typographer() { function Typographer() {
this._rules = [];
this.options = {}; this.options = {};
this.ruler = new Ruler(this.rulesUpdate.bind(this)); this.ruler = new Ruler();
for (var i = 0; i < _rules.length; i++) { for (var i = 0; i < _rules.length; i++) {
this.ruler.push(_rules[i][0], _rules[i][1]); this.ruler.push(_rules[i][0], _rules[i][1]);
@ -30,11 +28,6 @@ function Typographer() {
} }
Typographer.prototype.rulesUpdate = function () {
this._rules = this.ruler.getRules();
};
Typographer.prototype.set = function (options) { Typographer.prototype.set = function (options) {
assign(this.options, options); assign(this.options, options);
}; };
@ -43,7 +36,7 @@ Typographer.prototype.set = function (options) {
Typographer.prototype.process = function (state) { Typographer.prototype.process = function (state) {
var i, l, rules; var i, l, rules;
rules = this._rules; rules = this.ruler.getRules('');
for (i = 0, l = rules.length; i < l; i++) { for (i = 0, l = rules.length; i < l; i++) {
rules[i](this, state); rules[i](this, state);

Loading…
Cancel
Save