Reimplement MD044/proper-names using micromark tokens.

This commit is contained in:
David Anson 2023-03-14 21:03:07 -07:00
parent e8a85c91f2
commit 5bff33d41b
9 changed files with 182 additions and 495 deletions

View file

@ -15,15 +15,16 @@ module.exports = {
filterByPredicate(
params.parsers.micromark.tokens,
(token) => token.type === "literalAutolink",
(tokens) => {
(token) => {
const { children } = token;
const result = [];
for (let i = 0; i < tokens.length; i++) {
const openToken = tokens[i];
for (let i = 0; i < children.length; i++) {
const openToken = children[i];
const openTagInfo = getHtmlTagInfo(openToken);
if (openTagInfo && !openTagInfo.close) {
let count = 1;
for (let j = i + 1; j < tokens.length; j++) {
const closeToken = tokens[j];
for (let j = i + 1; j < children.length; j++) {
const closeToken = children[j];
const closeTagInfo = getHtmlTagInfo(closeToken);
if (closeTagInfo && (openTagInfo.name === closeTagInfo.name)) {
if (closeTagInfo.close) {

View file

@ -2,11 +2,14 @@
"use strict";
const { addErrorDetailIf, escapeForRegExp, forEachLine, forEachLink,
funcExpExec, linkReferenceDefinitionRe, urlFe, withinAnyRange } =
const { addErrorDetailIf, escapeForRegExp, withinAnyRange } =
require("../helpers");
const { codeBlockAndSpanRanges, htmlElementRanges, lineMetadata } =
require("./cache");
const { filterByPredicate, filterByTypes, parse } =
require("../helpers/micromark.cjs");
const ignoredChildTypes = new Set(
[ "codeFencedFence", "definition", "reference", "resource" ]
);
module.exports = {
"names": [ "MD044", "proper-names" ],
@ -22,31 +25,25 @@ module.exports = {
const htmlElements = params.config.html_elements;
const includeHtmlElements =
(htmlElements === undefined) ? true : !!htmlElements;
const exclusions = [];
forEachLine(lineMetadata(), (line, lineIndex) => {
if (linkReferenceDefinitionRe.test(line)) {
exclusions.push([ lineIndex, 0, line.length ]);
} else {
let match = null;
while ((match = funcExpExec(urlFe, line)) !== null) {
// @ts-ignore
exclusions.push([ lineIndex, match.index, match[0].length ]);
}
forEachLink(line, (index, _, text, destination) => {
if (destination) {
exclusions.push(
[ lineIndex, index + text.length, destination.length ]
);
const scannedTypes = new Set([ "data", "htmlFlowData" ]);
if (includeCodeBlocks) {
scannedTypes.add("codeFlowValue");
scannedTypes.add("codeTextData");
}
const contentTokens =
filterByPredicate(
params.parsers.micromark.tokens,
(token) => scannedTypes.has(token.type),
(token) => {
let { children } = token;
if (!includeHtmlElements && (token.type === "htmlFlow")) {
children = children.slice(1, -1);
}
});
}
});
if (!includeCodeBlocks) {
exclusions.push(...codeBlockAndSpanRanges());
}
if (!includeHtmlElements) {
exclusions.push(...htmlElementRanges());
}
return children.filter((t) => !ignoredChildTypes.has(t.type));
}
);
const exclusions = [];
const autoLinked = new Set();
for (const name of names) {
const escapedName = escapeForRegExp(name);
const startNamePattern = /^\W/.test(name) ? "" : "\\b_*";
@ -54,36 +51,53 @@ module.exports = {
const namePattern =
`(${startNamePattern})(${escapedName})${endNamePattern}`;
const nameRe = new RegExp(namePattern, "gi");
forEachLine(lineMetadata(), (line, lineIndex, inCode, onFence) => {
if (includeCodeBlocks || (!inCode && !onFence)) {
let match = null;
while ((match = nameRe.exec(line)) !== null) {
const [ , leftMatch, nameMatch ] = match;
const index = match.index + leftMatch.length;
const length = nameMatch.length;
if (
!withinAnyRange(exclusions, lineIndex, index, length) &&
!names.includes(nameMatch)
) {
for (const token of contentTokens) {
let match = null;
while ((match = nameRe.exec(token.text)) !== null) {
const [ , leftMatch, nameMatch ] = match;
const index = token.startColumn - 1 + match.index + leftMatch.length;
const length = nameMatch.length;
const lineIndex = token.startLine - 1;
if (
!withinAnyRange(exclusions, lineIndex, index, length) &&
!names.includes(nameMatch)
) {
let urlRanges = [];
if (!autoLinked.has(token)) {
urlRanges = filterByTypes(
parse(token.text),
[ "literalAutolink" ]
).map(
(t) => [
lineIndex,
token.startColumn - 1 + t.startColumn - 1,
t.endColumn - t.startColumn
]
);
exclusions.push(...urlRanges);
autoLinked.add(token);
}
if (!withinAnyRange(urlRanges, lineIndex, index, length)) {
const column = index + 1;
addErrorDetailIf(
onError,
lineIndex + 1,
token.startLine,
name,
nameMatch,
null,
null,
[ index + 1, length ],
[ column, length ],
{
"editColumn": index + 1,
"editColumn": column,
"deleteCount": length,
"insertText": name
}
);
}
exclusions.push([ lineIndex, index, length ]);
}
exclusions.push([ lineIndex, index, length ]);
}
});
}
}
}
};