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

@ -117,51 +117,104 @@ module.exports.headingStyleFor = function headingStyleFor(token) {
// Calls the provided function for each matching token
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;
var lastForEachLineParams = null;
var lastForEachLineResult = null;
var tokenCache = null;
// Caches line metadata and flattened lists for reuse
function makeTokenCache(params) {
if (!params) {
tokenCache = null;
return;
}
// Populate line metadata array
var lineMetadata = new Array(params.lines.length);
var fenceStart = null;
var inFence = false;
// Find fenced code by pattern (parser ignores "``` close fence")
params.lines.forEach(function forLine(line, lineIndex) {
var metadata = 0;
var match = /^(`{3,}|~{3,})/.exec(line);
var fence = match && match[1];
if (fence &&
(!inFence || (fence.substr(0, fenceStart.length) === fenceStart))) {
metadata = inFence ? 2 : 6;
fenceStart = inFence ? null : fence;
inFence = !inFence;
} else if (inFence) {
metadata = 1;
}
lineMetadata[lineIndex] = metadata;
});
// Find code blocks normally
filterTokens(params, "code_block", function forToken(token) {
for (var i = token.map[0]; i < token.map[1]; i++) {
lineMetadata[i] = 1;
}
});
// Find tables normally
filterTokens(params, "table_open", function forToken(token) {
for (var i = token.map[0]; i < token.map[1]; i++) {
lineMetadata[i] += 8;
}
});
// 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) {
if (params !== lastForEachLineParams) {
var lineMetadata = new Array(params.lines.length);
var fenceStart = null;
var inFence = false;
// Find fenced code by pattern (parser ignores "``` close fence")
params.lines.forEach(function forLine(line, lineIndex) {
var metadata = 0;
var match = /^(`{3,}|~{3,})/.exec(line);
var fence = match && match[1];
if (fence &&
(!inFence || (fence.substr(0, fenceStart.length) === fenceStart))) {
metadata = inFence ? 2 : 6;
fenceStart = inFence ? null : fence;
inFence = !inFence;
} else if (inFence) {
metadata = 1;
}
lineMetadata[lineIndex] = metadata;
});
// Find code blocks normally
filterTokens(params, "code_block", function forToken(token) {
for (var i = token.map[0]; i < token.map[1]; i++) {
lineMetadata[i] = 1;
}
});
// Find tables normally
filterTokens(params, "table_open", function forToken(token) {
for (var i = token.map[0]; i < token.map[1]; i++) {
lineMetadata[i] += 8;
}
});
lastForEachLineParams = params;
lastForEachLineResult = lineMetadata;
}
// Invoke callback
params.lines.forEach(function forLine(line, lineIndex) {
var metadata = lastForEachLineResult[lineIndex];
tokenCache.params.lines.forEach(function forLine(line, lineIndex) {
var metadata = tokenCache.lineMetadata[lineIndex];
callback(
line,
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)
module.exports.flattenLists = function flattenLists(params) {
if (lastFlattenListsParams !== params) {
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;
module.exports.flattenLists = function flattenLists() {
return tokenCache.flattenedLists;
};
// Adds a generic error object via the onError callback