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() {
this._rules = [];
this.options = {};
this.ruler = new Ruler(this.rulesUpdate.bind(this));
this.ruler = new Ruler();
for (var i = 0; i < _rules.length; i++) {
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) {
assign(this.options, options);
};
@ -38,7 +31,7 @@ Linkifier.prototype.set = function (options) {
Linkifier.prototype.process = function (state) {
var i, l, rules;
rules = this._rules;
rules = this.ruler.getRules('');
for (i = 0, l = rules.length; i < l; i++) {
rules[i](this, state);

19
lib/parser_block.js

@ -25,12 +25,7 @@ var _rules = [
// Block Parser class
//
function ParserBlock() {
this._rules = [];
this._rulesParagraphTerm = [];
this._rulesBlockquoteTerm = [];
this._rulesListTerm = [];
this.ruler = new Ruler(this.rulesUpdate.bind(this));
this.ruler = new Ruler();
for (var i = 0; i < _rules.length; i++) {
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
//
ParserBlock.prototype.tokenize = function (state, startLine, endLine) {
var ok, i,
rules = this._rules,
len = this._rules.length,
rules = this.ruler.getRules(''),
len = rules.length,
line = startLine,
hasEmptyLines = false;

18
lib/parser_inline.js

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

88
lib/ruler.js

@ -9,9 +9,7 @@
////////////////////////////////////////////////////////////////////////////////
function Ruler(compileFn) {
this.compile = compileFn; // callback to call after each change
function Ruler() {
// List of added rules. Each element is:
//
// {
@ -22,6 +20,13 @@ function Ruler(compileFn) {
// }
//
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].alt = opt.alt || [];
this.compile();
this.cache = null;
};
@ -66,7 +71,7 @@ Ruler.prototype.before = function (beforeName, ruleName, fn, options) {
alt: opt.alt || []
});
this.compile();
this.cache = null;
};
@ -85,7 +90,7 @@ Ruler.prototype.after = function (afterName, ruleName, fn, options) {
alt: opt.alt || []
});
this.compile();
this.cache = null;
};
// Add rule to the end of chain.
@ -100,30 +105,7 @@ Ruler.prototype.push = function (ruleName, fn, options) {
alt: opt.alt || []
});
this.compile();
};
// 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;
this.cache = null;
};
@ -151,7 +133,7 @@ Ruler.prototype.enable = function (list, strict) {
}, this);
this.compile();
this.cache = null;
};
@ -171,8 +153,50 @@ Ruler.prototype.disable = function (list) {
}, 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;

5
lib/rules_block/blockquote.js

@ -5,7 +5,8 @@
module.exports = function blockquote(state, startLine, endLine, silent) {
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],
max = state.eMarks[startLine];
@ -36,6 +37,8 @@ module.exports = function blockquote(state, startLine, endLine, silent) {
oldTShift = [ state.tShift[startLine] ];
state.tShift[startLine] = pos - state.bMarks[startLine];
terminatorRules = state.parser.ruler.getRules('blockquote');
// Search the end of the block
//
// Block ends with either:

3
lib/rules_block/list.js

@ -102,7 +102,7 @@ module.exports = function list(state, startLine, endLine, silent) {
listLines,
itemLines,
tight = true,
terminatorRules = state.parser._rulesListTerm,
terminatorRules,
i, l, terminate;
// Detect list type and position after marker
@ -150,6 +150,7 @@ module.exports = function list(state, startLine, endLine, silent) {
nextLine = startLine;
prevEmptyEnd = false;
terminatorRules = state.parser.ruler.getRules('list');
while (nextLine < endLine) {
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*/) {
var endLine, content, pos, terminate, i, l,
nextLine = startLine + 1,
terminatorRules = state.parser._rulesParagraphTerm;
terminatorRules;
endLine = state.lineMax;
// jump line-by-line until empty one or EOF
for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {
// this would be a code block normally, but after paragraph
// it's considered a lazy continuation regardless of what's there
if (state.tShift[nextLine] - state.blkIndent > 3) { continue; }
if (nextLine < endLine && !state.isEmpty(nextLine)) {
terminatorRules = state.parser.ruler.getRules('paragraph');
// 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;
for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {
// this would be a code block normally, but after 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.
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();

11
lib/typographer.js

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

Loading…
Cancel
Save