Remove params.tokenLists to simplify custom rule API.

This commit is contained in:
David Anson 2018-03-01 22:37:37 -08:00
parent 7a752784f1
commit b33acb81d9
2 changed files with 100 additions and 94 deletions

View file

@ -141,7 +141,6 @@ function removeFrontMatter(content, frontMatter) {
// Annotate tokens with line/lineNumber // Annotate tokens with line/lineNumber
function annotateTokens(tokens, lines) { function annotateTokens(tokens, lines) {
var tbodyMap = null; var tbodyMap = null;
var tokenLists = {};
tokens.forEach(function forToken(token) { tokens.forEach(function forToken(token) {
// Handle missing maps for table body // Handle missing maps for table body
if (token.type === "tbody_open") { if (token.type === "tbody_open") {
@ -172,12 +171,7 @@ function annotateTokens(tokens, lines) {
} }
}); });
} }
if (!tokenLists[token.type]) {
tokenLists[token.type] = [];
}
tokenLists[token.type].push(token);
}); });
return tokenLists;
} }
// Map rule names/tags to canonical rule name // Map rule names/tags to canonical rule name
@ -292,19 +286,16 @@ function lintContent(
ruleList, content, config, frontMatter, noInlineConfig, resultVersion, ruleList, content, config, frontMatter, noInlineConfig, resultVersion,
callback) { callback) {
// Remove UTF-8 byte order marker (if present) // Remove UTF-8 byte order marker (if present)
if (content.charCodeAt(0) === 0xfeff) { content = content.replace(/^\ufeff/, "");
content = content.slice(1);
}
// Remove front matter // Remove front matter
var removeFrontMatterResult = removeFrontMatter(content, frontMatter); var removeFrontMatterResult = removeFrontMatter(content, frontMatter);
content = removeFrontMatterResult.content;
var frontMatterLines = removeFrontMatterResult.frontMatterLines; var frontMatterLines = removeFrontMatterResult.frontMatterLines;
// Ignore the content of HTML comments // Ignore the content of HTML comments
content = shared.clearHtmlCommentText(content); content = shared.clearHtmlCommentText(removeFrontMatterResult.content);
// Parse content into tokens and lines // Parse content into tokens and lines
var tokens = md.parse(content, {}); var tokens = md.parse(content, {});
var lines = content.split(shared.newLineRe); var lines = content.split(shared.newLineRe);
var tokenLists = annotateTokens(tokens, lines); annotateTokens(tokens, lines);
var aliasToRuleNames = mapAliasToRuleNames(ruleList); var aliasToRuleNames = mapAliasToRuleNames(ruleList);
var effectiveConfig = getEffectiveConfig(ruleList, config, aliasToRuleNames); var effectiveConfig = getEffectiveConfig(ruleList, config, aliasToRuleNames);
var enabledRulesPerLineNumber = getEnabledRulesPerLineNumber( var enabledRulesPerLineNumber = getEnabledRulesPerLineNumber(
@ -313,10 +304,10 @@ function lintContent(
// Create parameters for rules // Create parameters for rules
var params = { var params = {
"tokens": tokens, "tokens": tokens,
"tokenLists": tokenLists,
"lines": lines, "lines": lines,
"frontMatterLines": frontMatterLines "frontMatterLines": frontMatterLines
}; };
shared.makeTokenCache(params);
// Function to run for each rule // Function to run for each rule
var result = (resultVersion === 0) ? {} : []; var result = (resultVersion === 0) ? {} : [];
function forRule(rule) { function forRule(rule) {
@ -402,8 +393,10 @@ function lintContent(
try { try {
ruleList.forEach(forRule); ruleList.forEach(forRule);
} catch (ex) { } catch (ex) {
shared.makeTokenCache(null);
return callback(ex); return callback(ex);
} }
shared.makeTokenCache(null);
callback(null, result); callback(null, result);
} }

View file

@ -117,15 +117,23 @@ module.exports.headingStyleFor = function headingStyleFor(token) {
// Calls the provided function for each matching token // Calls the provided function for each matching token
function filterTokens(params, type, callback) { function filterTokens(params, type, callback) {
(params.tokenLists[type] || []).forEach(callback); params.tokens.forEach(function forToken(token) {
if (token.type === type) {
callback(token);
}
});
} }
module.exports.filterTokens = filterTokens; module.exports.filterTokens = filterTokens;
var lastForEachLineParams = null; var tokenCache = null;
var lastForEachLineResult = null; // Caches line metadata and flattened lists for reuse
// Calls the provided function for each line (with context) function makeTokenCache(params) {
module.exports.forEachLine = function forEachLine(params, callback) { if (!params) {
if (params !== lastForEachLineParams) { tokenCache = null;
return;
}
// Populate line metadata array
var lineMetadata = new Array(params.lines.length); var lineMetadata = new Array(params.lines.length);
var fenceStart = null; var fenceStart = null;
var inFence = false; var inFence = false;
@ -156,12 +164,57 @@ module.exports.forEachLine = function forEachLine(params, callback) {
lineMetadata[i] += 8; lineMetadata[i] += 8;
} }
}); });
lastForEachLineParams = params;
lastForEachLineResult = lineMetadata; // Flatten lists
var flattenedLists = [];
var stack = [];
var current = null;
var lastWithMap = { "map": [ 0, 1 ] };
params.tokens.forEach(function forToken(token) {
if ((token.type === "bullet_list_open") ||
(token.type === "ordered_list_open")) {
// Save current context and start a new one
stack.push(current);
current = {
"unordered": (token.type === "bullet_list_open"),
"parentsUnordered": !current ||
(current.unordered && current.parentsUnordered),
"open": token,
"items": [],
"nesting": stack.length - 1,
"lastLineIndex": -1,
"insert": flattenedLists.length
};
} else if ((token.type === "bullet_list_close") ||
(token.type === "ordered_list_close")) {
// Finalize current context and restore previous
current.lastLineIndex = lastWithMap.map[1];
flattenedLists.splice(current.insert, 0, current);
delete current.insert;
current = stack.pop();
} else if (token.type === "list_item_open") {
// Add list item
current.items.push(token);
} else if (token.map) {
// Track last token with map
lastWithMap = token;
} }
});
// Cache results
tokenCache = {
"params": params,
"lineMetadata": lineMetadata,
"flattenedLists": flattenedLists
};
}
module.exports.makeTokenCache = makeTokenCache;
// Calls the provided function for each line (with context)
module.exports.forEachLine = function forEachLine(params, callback) {
// Invoke callback // Invoke callback
params.lines.forEach(function forLine(line, lineIndex) { tokenCache.params.lines.forEach(function forLine(line, lineIndex) {
var metadata = lastForEachLineResult[lineIndex]; var metadata = tokenCache.lineMetadata[lineIndex];
callback( callback(
line, line,
lineIndex, lineIndex,
@ -197,49 +250,9 @@ module.exports.forEachHeading = function forEachHeading(params, callback) {
}); });
}; };
var lastFlattenListsParams = null;
var lastFlattenListsResult = null;
// Returns (nested) lists as a flat array (in order) // Returns (nested) lists as a flat array (in order)
module.exports.flattenLists = function flattenLists(params) { module.exports.flattenLists = function flattenLists() {
if (lastFlattenListsParams !== params) { return tokenCache.flattenedLists;
var lists = [];
var stack = [];
var current = null;
var lastWithMap = { "map": [ 0, 1 ] };
params.tokens.forEach(function forToken(token) {
if ((token.type === "bullet_list_open") ||
(token.type === "ordered_list_open")) {
// Save current context and start a new one
stack.push(current);
current = {
"unordered": (token.type === "bullet_list_open"),
"parentsUnordered": !current ||
(current.unordered && current.parentsUnordered),
"open": token,
"items": [],
"nesting": stack.length - 1,
"lastLineIndex": -1,
"insert": lists.length
};
} else if ((token.type === "bullet_list_close") ||
(token.type === "ordered_list_close")) {
// Finalize current context and restore previous
current.lastLineIndex = lastWithMap.map[1];
lists.splice(current.insert, 0, current);
delete current.insert;
current = stack.pop();
} else if (token.type === "list_item_open") {
// Add list item
current.items.push(token);
} else if (token.map) {
// Track last token with map
lastWithMap = token;
}
});
lastFlattenListsParams = params;
lastFlattenListsResult = lists;
}
return lastFlattenListsResult;
}; };
// Adds a generic error object via the onError callback // Adds a generic error object via the onError callback