markdownlint 0.29.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/demo/markdownlint-browser.js +997 -891
- package/doc/CustomRules.md +2 -0
- package/doc/Rules.md +46 -23
- package/doc/md022.md +12 -6
- package/doc/md026.md +1 -2
- package/doc/md049.md +2 -2
- package/doc/md050.md +2 -2
- package/doc/md051.md +17 -6
- package/doc/md052.md +13 -6
- package/helpers/helpers.js +146 -235
- package/helpers/micromark.cjs +192 -53
- package/helpers/package.json +2 -2
- package/helpers/shared.js +7 -0
- package/lib/cache.js +0 -2
- package/lib/constants.js +1 -1
- package/lib/markdownlint.d.ts +10 -2
- package/lib/markdownlint.js +37 -13
- package/lib/md022.js +80 -40
- package/lib/md026.js +19 -12
- package/lib/md032.js +62 -31
- package/lib/md033.js +20 -45
- package/lib/md037.js +70 -164
- package/lib/md043.js +42 -40
- package/lib/md044.js +15 -41
- package/lib/md045.js +17 -5
- package/lib/md049-md050.js +6 -3
- package/lib/md051.js +123 -72
- package/lib/md052.js +7 -3
- package/package.json +25 -24
- package/schema/.markdownlint.jsonc +7 -4
- package/schema/.markdownlint.yaml +6 -4
- package/schema/markdownlint-config-schema.json +32 -9
package/lib/md022.js
CHANGED
|
@@ -2,8 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addErrorDetailIf, blockquotePrefixRe,
|
|
5
|
+
const { addErrorDetailIf, blockquotePrefixRe, isBlankLine } =
|
|
6
6
|
require("../helpers");
|
|
7
|
+
const { filterByTypes, getHeadingLevel } =
|
|
8
|
+
require("../helpers/micromark.cjs");
|
|
9
|
+
|
|
10
|
+
const defaultLines = 1;
|
|
11
|
+
|
|
12
|
+
const getLinesFunction = (linesParam) => {
|
|
13
|
+
if (Array.isArray(linesParam)) {
|
|
14
|
+
const linesArray = new Array(6).fill(defaultLines);
|
|
15
|
+
for (const [ index, value ] of [ ...linesParam.entries() ].slice(0, 6)) {
|
|
16
|
+
linesArray[index] = value;
|
|
17
|
+
}
|
|
18
|
+
return (heading) => linesArray[getHeadingLevel(heading) - 1];
|
|
19
|
+
}
|
|
20
|
+
// Coerce linesParam to a number
|
|
21
|
+
const lines = (linesParam === undefined) ? defaultLines : Number(linesParam);
|
|
22
|
+
return () => lines;
|
|
23
|
+
};
|
|
7
24
|
|
|
8
25
|
const getBlockQuote = (str, count) => (
|
|
9
26
|
(str || "")
|
|
@@ -19,50 +36,73 @@ module.exports = {
|
|
|
19
36
|
"description": "Headings should be surrounded by blank lines",
|
|
20
37
|
"tags": [ "headings", "headers", "blank_lines" ],
|
|
21
38
|
"function": function MD022(params, onError) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
39
|
+
const getLinesAbove = getLinesFunction(params.config.lines_above);
|
|
40
|
+
const getLinesBelow = getLinesFunction(params.config.lines_below);
|
|
41
|
+
const { lines, parsers } = params;
|
|
42
|
+
const headings = filterByTypes(
|
|
43
|
+
parsers.micromark.tokens,
|
|
44
|
+
[ "atxHeading", "setextHeading" ]
|
|
45
|
+
);
|
|
46
|
+
for (const heading of headings) {
|
|
47
|
+
const { startLine, endLine } = heading;
|
|
48
|
+
const line = lines[startLine - 1].trim();
|
|
49
|
+
|
|
50
|
+
// Check lines above
|
|
51
|
+
const linesAbove = getLinesAbove(heading);
|
|
52
|
+
if (linesAbove >= 0) {
|
|
53
|
+
let actualAbove = 0;
|
|
54
|
+
for (
|
|
55
|
+
let i = 0;
|
|
56
|
+
(i < linesAbove) && isBlankLine(lines[startLine - 2 - i]);
|
|
57
|
+
i++
|
|
58
|
+
) {
|
|
32
59
|
actualAbove++;
|
|
33
60
|
}
|
|
61
|
+
addErrorDetailIf(
|
|
62
|
+
onError,
|
|
63
|
+
startLine,
|
|
64
|
+
linesAbove,
|
|
65
|
+
actualAbove,
|
|
66
|
+
"Above",
|
|
67
|
+
line,
|
|
68
|
+
null,
|
|
69
|
+
{
|
|
70
|
+
"insertText": getBlockQuote(
|
|
71
|
+
lines[startLine - 2],
|
|
72
|
+
linesAbove - actualAbove
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
);
|
|
34
76
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
getBlockQuote(lines[topIndex - 1], linesAbove - actualAbove)
|
|
46
|
-
});
|
|
47
|
-
let actualBelow = 0;
|
|
48
|
-
for (let i = 0; i < linesBelow; i++) {
|
|
49
|
-
if (isBlankLine(lines[nextIndex + i])) {
|
|
77
|
+
|
|
78
|
+
// Check lines below
|
|
79
|
+
const linesBelow = getLinesBelow(heading);
|
|
80
|
+
if (linesBelow >= 0) {
|
|
81
|
+
let actualBelow = 0;
|
|
82
|
+
for (
|
|
83
|
+
let i = 0;
|
|
84
|
+
(i < linesBelow) && isBlankLine(lines[endLine + i]);
|
|
85
|
+
i++
|
|
86
|
+
) {
|
|
50
87
|
actualBelow++;
|
|
51
88
|
}
|
|
89
|
+
addErrorDetailIf(
|
|
90
|
+
onError,
|
|
91
|
+
startLine,
|
|
92
|
+
linesBelow,
|
|
93
|
+
actualBelow,
|
|
94
|
+
"Below",
|
|
95
|
+
line,
|
|
96
|
+
null,
|
|
97
|
+
{
|
|
98
|
+
"lineNumber": endLine + 1,
|
|
99
|
+
"insertText": getBlockQuote(
|
|
100
|
+
lines[endLine],
|
|
101
|
+
linesBelow - actualBelow
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
);
|
|
52
105
|
}
|
|
53
|
-
|
|
54
|
-
onError,
|
|
55
|
-
topIndex + 1,
|
|
56
|
-
linesBelow,
|
|
57
|
-
actualBelow,
|
|
58
|
-
"Below",
|
|
59
|
-
lines[topIndex].trim(),
|
|
60
|
-
null,
|
|
61
|
-
{
|
|
62
|
-
"lineNumber": nextIndex + 1,
|
|
63
|
-
"insertText":
|
|
64
|
-
getBlockQuote(lines[nextIndex], linesBelow - actualBelow)
|
|
65
|
-
});
|
|
66
|
-
});
|
|
106
|
+
}
|
|
67
107
|
}
|
|
68
108
|
};
|
package/lib/md026.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addError, allPunctuationNoQuestion,
|
|
6
|
-
require("../helpers");
|
|
5
|
+
const { addError, allPunctuationNoQuestion, endOfLineGemojiCodeRe,
|
|
6
|
+
endOfLineHtmlEntityRe, escapeForRegExp } = require("../helpers");
|
|
7
|
+
const { filterByTypes } = require("../helpers/micromark.cjs");
|
|
7
8
|
|
|
8
|
-
const endOfLineHtmlEntityRe = /&#?[\da-zA-Z]+;$/;
|
|
9
9
|
|
|
10
10
|
module.exports = {
|
|
11
11
|
"names": [ "MD026", "no-trailing-punctuation" ],
|
|
@@ -18,19 +18,26 @@ module.exports = {
|
|
|
18
18
|
);
|
|
19
19
|
const trailingPunctuationRe =
|
|
20
20
|
new RegExp("\\s*[" + escapeForRegExp(punctuation) + "]+$");
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
const headings = filterByTypes(
|
|
22
|
+
params.parsers.micromark.tokens,
|
|
23
|
+
[ "atxHeadingText", "setextHeadingText" ]
|
|
24
|
+
);
|
|
25
|
+
for (const heading of headings) {
|
|
26
|
+
const { endLine, startColumn, text } = heading;
|
|
27
|
+
const match = trailingPunctuationRe.exec(text);
|
|
28
|
+
if (
|
|
29
|
+
match &&
|
|
30
|
+
!endOfLineHtmlEntityRe.test(text) &&
|
|
31
|
+
!endOfLineGemojiCodeRe.test(text)
|
|
32
|
+
) {
|
|
26
33
|
const fullMatch = match[0];
|
|
27
|
-
const column = match.index
|
|
34
|
+
const column = startColumn + match.index;
|
|
28
35
|
const length = fullMatch.length;
|
|
29
36
|
addError(
|
|
30
37
|
onError,
|
|
31
|
-
|
|
38
|
+
endLine,
|
|
32
39
|
`Punctuation: '${fullMatch}'`,
|
|
33
|
-
|
|
40
|
+
undefined,
|
|
34
41
|
[ column, length ],
|
|
35
42
|
{
|
|
36
43
|
"editColumn": column,
|
|
@@ -38,6 +45,6 @@ module.exports = {
|
|
|
38
45
|
}
|
|
39
46
|
);
|
|
40
47
|
}
|
|
41
|
-
}
|
|
48
|
+
}
|
|
42
49
|
}
|
|
43
50
|
};
|
package/lib/md032.js
CHANGED
|
@@ -4,46 +4,77 @@
|
|
|
4
4
|
|
|
5
5
|
const { addErrorContext, blockquotePrefixRe, isBlankLine } =
|
|
6
6
|
require("../helpers");
|
|
7
|
-
const {
|
|
7
|
+
const { filterByPredicate } = require("../helpers/micromark.cjs");
|
|
8
|
+
|
|
9
|
+
const nonContentTokens = new Set([
|
|
10
|
+
"blockQuoteMarker",
|
|
11
|
+
"blockQuotePrefix",
|
|
12
|
+
"blockQuotePrefixWhitespace",
|
|
13
|
+
"lineEnding",
|
|
14
|
+
"lineEndingBlank",
|
|
15
|
+
"linePrefix",
|
|
16
|
+
"listItemIndent"
|
|
17
|
+
]);
|
|
18
|
+
const isList = (token) => (
|
|
19
|
+
(token.type === "listOrdered") || (token.type === "listUnordered")
|
|
20
|
+
);
|
|
21
|
+
const addBlankLineError = (onError, lines, lineIndex, lineNumber) => {
|
|
22
|
+
const line = lines[lineIndex];
|
|
23
|
+
const quotePrefix = line.match(blockquotePrefixRe)[0].trimEnd();
|
|
24
|
+
addErrorContext(
|
|
25
|
+
onError,
|
|
26
|
+
lineIndex + 1,
|
|
27
|
+
line.trim(),
|
|
28
|
+
null,
|
|
29
|
+
null,
|
|
30
|
+
null,
|
|
31
|
+
{
|
|
32
|
+
lineNumber,
|
|
33
|
+
"insertText": `${quotePrefix}\n`
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
};
|
|
8
37
|
|
|
9
38
|
module.exports = {
|
|
10
39
|
"names": [ "MD032", "blanks-around-lists" ],
|
|
11
40
|
"description": "Lists should be surrounded by blank lines",
|
|
12
41
|
"tags": [ "bullet", "ul", "ol", "blank_lines" ],
|
|
13
42
|
"function": function MD032(params, onError) {
|
|
14
|
-
const { lines } = params;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
43
|
+
const { lines, parsers } = params;
|
|
44
|
+
|
|
45
|
+
// For every top-level list...
|
|
46
|
+
const topLevelLists = filterByPredicate(
|
|
47
|
+
parsers.micromark.tokens,
|
|
48
|
+
isList,
|
|
49
|
+
(token) => (
|
|
50
|
+
(isList(token) || (token.type === "htmlFlow")) ? [] : token.children
|
|
51
|
+
)
|
|
52
|
+
);
|
|
53
|
+
for (const list of topLevelLists) {
|
|
54
|
+
|
|
55
|
+
// Look for a blank line above the list
|
|
56
|
+
const firstIndex = list.startLine - 1;
|
|
18
57
|
if (!isBlankLine(lines[firstIndex - 1])) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
58
|
+
addBlankLineError(onError, lines, firstIndex);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Find the "visual" end of the list
|
|
62
|
+
let endLine = list.endLine;
|
|
63
|
+
const flattenedChildren = filterByPredicate(
|
|
64
|
+
list.children,
|
|
65
|
+
() => true
|
|
66
|
+
);
|
|
67
|
+
for (const child of flattenedChildren.reverse()) {
|
|
68
|
+
if (!nonContentTokens.has(child.type)) {
|
|
69
|
+
endLine = child.endLine;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
31
72
|
}
|
|
32
|
-
|
|
73
|
+
|
|
74
|
+
// Look for a blank line below the list
|
|
75
|
+
const lastIndex = endLine - 1;
|
|
33
76
|
if (!isBlankLine(lines[lastIndex + 1])) {
|
|
34
|
-
|
|
35
|
-
const quotePrefix = line.match(blockquotePrefixRe)[0].trimEnd();
|
|
36
|
-
addErrorContext(
|
|
37
|
-
onError,
|
|
38
|
-
lastIndex + 1,
|
|
39
|
-
line.trim(),
|
|
40
|
-
null,
|
|
41
|
-
null,
|
|
42
|
-
null,
|
|
43
|
-
{
|
|
44
|
-
"lineNumber": lastIndex + 2,
|
|
45
|
-
"insertText": `${quotePrefix}\n`
|
|
46
|
-
});
|
|
77
|
+
addBlankLineError(onError, lines, lastIndex, lastIndex + 2);
|
|
47
78
|
}
|
|
48
79
|
}
|
|
49
80
|
}
|
package/lib/md033.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
5
|
const { addError } = require("../helpers");
|
|
6
|
-
const { filterByTypes, getHtmlTagInfo
|
|
6
|
+
const { filterByTypes, getHtmlTagInfo } =
|
|
7
7
|
require("../helpers/micromark.cjs");
|
|
8
8
|
|
|
9
9
|
const nextLinesRe = /[\r\n][\s\S]*$/;
|
|
@@ -16,50 +16,25 @@ module.exports = {
|
|
|
16
16
|
let allowedElements = params.config.allowed_elements;
|
|
17
17
|
allowedElements = Array.isArray(allowedElements) ? allowedElements : [];
|
|
18
18
|
allowedElements = allowedElements.map((element) => element.toLowerCase());
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"Element: " + htmlTagInfo.name,
|
|
39
|
-
undefined,
|
|
40
|
-
range
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
} else {
|
|
44
|
-
// token.type === "htmlFlow"
|
|
45
|
-
// Re-parse without "htmlFlow" to get only "htmlText" tokens
|
|
46
|
-
const options = {
|
|
47
|
-
"extensions": [
|
|
48
|
-
{
|
|
49
|
-
"disable": {
|
|
50
|
-
"null": [ "codeIndented", "htmlFlow" ]
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
]
|
|
54
|
-
};
|
|
55
|
-
// Use lines instead of token.text for accurate columns
|
|
56
|
-
const lines =
|
|
57
|
-
params.lines.slice(token.startLine - 1, token.endLine).join("\n");
|
|
58
|
-
const flowTokens = parse(lines, options);
|
|
59
|
-
pending.push(
|
|
60
|
-
[ token.startLine - 1, flowTokens ]
|
|
61
|
-
);
|
|
62
|
-
}
|
|
19
|
+
const { tokens } = params.parsers.micromark;
|
|
20
|
+
for (const token of filterByTypes(tokens, [ "htmlText" ])) {
|
|
21
|
+
const htmlTagInfo = getHtmlTagInfo(token);
|
|
22
|
+
if (
|
|
23
|
+
htmlTagInfo &&
|
|
24
|
+
!htmlTagInfo.close &&
|
|
25
|
+
!allowedElements.includes(htmlTagInfo.name.toLowerCase())
|
|
26
|
+
) {
|
|
27
|
+
const range = [
|
|
28
|
+
token.startColumn,
|
|
29
|
+
token.text.replace(nextLinesRe, "").length
|
|
30
|
+
];
|
|
31
|
+
addError(
|
|
32
|
+
onError,
|
|
33
|
+
token.startLine,
|
|
34
|
+
"Element: " + htmlTagInfo.name,
|
|
35
|
+
undefined,
|
|
36
|
+
range
|
|
37
|
+
);
|
|
63
38
|
}
|
|
64
39
|
}
|
|
65
40
|
}
|
package/lib/md037.js
CHANGED
|
@@ -2,184 +2,90 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const {
|
|
6
|
-
|
|
7
|
-
const { htmlElementRanges, lineMetadata } = require("./cache");
|
|
8
|
-
|
|
9
|
-
const emphasisRe = /(^|[^\\]|\\\\)(?:(\*{1,3})|(_{1,3}))/g;
|
|
10
|
-
const embeddedUnderscoreRe = /([A-Za-z\d])(_([A-Za-z\d]))+/g;
|
|
11
|
-
const asteriskListItemMarkerRe = /^([\s>]*)\*(\s+)/;
|
|
12
|
-
const leftSpaceRe = /^\s+/;
|
|
13
|
-
const rightSpaceRe = /\s+$/;
|
|
14
|
-
const tablePipeRe = /\|/;
|
|
15
|
-
const allUnderscoresRe = /_/g;
|
|
5
|
+
const { addError } = require("../helpers");
|
|
6
|
+
const { filterByPredicate } = require("../helpers/micromark.cjs");
|
|
16
7
|
|
|
17
8
|
module.exports = {
|
|
18
9
|
"names": [ "MD037", "no-space-in-emphasis" ],
|
|
19
10
|
"description": "Spaces inside emphasis markers",
|
|
20
11
|
"tags": [ "whitespace", "emphasis" ],
|
|
21
12
|
"function": function MD037(params, onError) {
|
|
22
|
-
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
emphasisIndex = -1;
|
|
29
|
-
emphasisLength = 0;
|
|
30
|
-
emphasisKind = "";
|
|
31
|
-
effectiveEmphasisLength = 0;
|
|
32
|
-
pendingError = null;
|
|
13
|
+
|
|
14
|
+
// Initialize variables
|
|
15
|
+
const { lines, parsers } = params;
|
|
16
|
+
const emphasisTokensByMarker = new Map();
|
|
17
|
+
for (const marker of [ "_", "__", "___", "*", "**", "***" ]) {
|
|
18
|
+
emphasisTokensByMarker.set(marker, []);
|
|
33
19
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (!match) {
|
|
44
|
-
content = content.trimEnd();
|
|
20
|
+
const tokens = filterByPredicate(
|
|
21
|
+
parsers.micromark.tokens,
|
|
22
|
+
(token) => token.children.some((child) => child.type === "data")
|
|
23
|
+
);
|
|
24
|
+
for (const token of tokens) {
|
|
25
|
+
|
|
26
|
+
// Build lists of bare tokens for each emphasis marker type
|
|
27
|
+
for (const emphasisTokens of emphasisTokensByMarker.values()) {
|
|
28
|
+
emphasisTokens.length = 0;
|
|
45
29
|
}
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const contextStart = emphasisIndex - emphasisLength;
|
|
54
|
-
const contextEnd = matchIndex + contextLength;
|
|
55
|
-
const column = contextStart + 1;
|
|
56
|
-
const length = contextEnd - contextStart;
|
|
57
|
-
if (!withinAnyRange(exclusions, lineIndex, column, length)) {
|
|
58
|
-
const context = line.substring(contextStart, contextEnd);
|
|
59
|
-
const leftMarker = line.substring(contextStart, emphasisIndex);
|
|
60
|
-
const rightMarker = match ? (match[2] || match[3]) : "";
|
|
61
|
-
const fixedText = `${leftMarker}${content.trim()}${rightMarker}`;
|
|
62
|
-
return [
|
|
63
|
-
onError,
|
|
64
|
-
lineIndex + 1,
|
|
65
|
-
context,
|
|
66
|
-
leftSpace,
|
|
67
|
-
rightSpace,
|
|
68
|
-
[ column, length ],
|
|
69
|
-
{
|
|
70
|
-
"editColumn": column,
|
|
71
|
-
"deleteCount": length,
|
|
72
|
-
"insertText": fixedText
|
|
73
|
-
}
|
|
74
|
-
];
|
|
30
|
+
for (const child of token.children) {
|
|
31
|
+
const { text, type } = child;
|
|
32
|
+
if ((type === "data") && (text.length <= 3)) {
|
|
33
|
+
const emphasisTokens = emphasisTokensByMarker.get(text);
|
|
34
|
+
if (emphasisTokens) {
|
|
35
|
+
emphasisTokens.push(child);
|
|
36
|
+
}
|
|
75
37
|
}
|
|
76
38
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
onBreak ||
|
|
101
|
-
inMath
|
|
102
|
-
) {
|
|
103
|
-
// Emphasis has no meaning here
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
let patchedLine = line.replace(
|
|
107
|
-
embeddedUnderscoreRe,
|
|
108
|
-
(match) => match.replace(allUnderscoresRe, " ")
|
|
109
|
-
);
|
|
110
|
-
if (onItemStart) {
|
|
111
|
-
// Trim overlapping '*' list item marker
|
|
112
|
-
patchedLine = patchedLine.replace(asteriskListItemMarkerRe, "$1 $2");
|
|
113
|
-
}
|
|
114
|
-
let match = null;
|
|
115
|
-
// Match all emphasis-looking runs in the line...
|
|
116
|
-
while ((match = emphasisRe.exec(patchedLine))) {
|
|
117
|
-
const ignoreMarkersForLine = ignoreMarkersByLine[lineIndex];
|
|
118
|
-
const matchIndex = match.index + match[1].length;
|
|
119
|
-
if (ignoreMarkersForLine.includes(matchIndex)) {
|
|
120
|
-
// Ignore emphasis markers inside code spans and links
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
|
-
const matchLength = match[0].length - match[1].length;
|
|
124
|
-
const matchKind = (match[2] || match[3])[0];
|
|
125
|
-
if (emphasisIndex === -1) {
|
|
126
|
-
// New run
|
|
127
|
-
emphasisIndex = matchIndex + matchLength;
|
|
128
|
-
emphasisLength = matchLength;
|
|
129
|
-
emphasisKind = matchKind;
|
|
130
|
-
effectiveEmphasisLength = matchLength;
|
|
131
|
-
} else if (matchKind === emphasisKind) {
|
|
132
|
-
// Matching emphasis markers
|
|
133
|
-
if (matchLength === effectiveEmphasisLength) {
|
|
134
|
-
// Ending an existing run, report any pending error
|
|
135
|
-
if (pendingError) {
|
|
136
|
-
// @ts-ignore
|
|
137
|
-
addErrorContext(...pendingError);
|
|
138
|
-
pendingError = null;
|
|
39
|
+
|
|
40
|
+
// Process bare tokens for each emphasis marker type
|
|
41
|
+
for (const entry of emphasisTokensByMarker.entries()) {
|
|
42
|
+
const [ marker, emphasisTokens ] = entry;
|
|
43
|
+
for (let i = 0; i + 1 < emphasisTokens.length; i += 2) {
|
|
44
|
+
|
|
45
|
+
// Process start token of start/end pair
|
|
46
|
+
const startToken = emphasisTokens[i];
|
|
47
|
+
const startLine = lines[startToken.startLine - 1];
|
|
48
|
+
const startSlice = startLine.slice(startToken.endColumn - 1);
|
|
49
|
+
const startMatch = startSlice.match(/^\s+\S/);
|
|
50
|
+
if (startMatch) {
|
|
51
|
+
const [ startSpaceCharacter ] = startMatch;
|
|
52
|
+
const startContext = `${marker}${startSpaceCharacter}`;
|
|
53
|
+
addError(
|
|
54
|
+
onError,
|
|
55
|
+
startToken.startLine,
|
|
56
|
+
undefined,
|
|
57
|
+
startContext,
|
|
58
|
+
[ startToken.startColumn, startContext.length ],
|
|
59
|
+
{
|
|
60
|
+
"editColumn": startToken.endColumn,
|
|
61
|
+
"deleteCount": startSpaceCharacter.length - 1
|
|
139
62
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Process end token of start/end pair
|
|
67
|
+
const endToken = emphasisTokens[i + 1];
|
|
68
|
+
const endLine = lines[endToken.startLine - 1];
|
|
69
|
+
const endSlice = endLine.slice(0, endToken.startColumn - 1);
|
|
70
|
+
const endMatch = endSlice.match(/\S\s+$/);
|
|
71
|
+
if (endMatch) {
|
|
72
|
+
const [ endSpaceCharacter ] = endMatch;
|
|
73
|
+
const endContext = `${endSpaceCharacter}${marker}`;
|
|
74
|
+
addError(
|
|
75
|
+
onError,
|
|
76
|
+
endToken.startLine,
|
|
77
|
+
undefined,
|
|
78
|
+
endContext,
|
|
79
|
+
[ endToken.endColumn - endContext.length, endContext.length ],
|
|
80
|
+
{
|
|
81
|
+
"editColumn":
|
|
82
|
+
endToken.startColumn - (endSpaceCharacter.length - 1),
|
|
83
|
+
"deleteCount": endSpaceCharacter.length - 1
|
|
151
84
|
}
|
|
152
|
-
|
|
153
|
-
resetRunTracking();
|
|
154
|
-
} else if (matchLength === 3) {
|
|
155
|
-
// Swap internal run length (1->2 or 2->1)
|
|
156
|
-
effectiveEmphasisLength = matchLength - effectiveEmphasisLength;
|
|
157
|
-
} else if (effectiveEmphasisLength === 3) {
|
|
158
|
-
// Downgrade internal run (3->1 or 3->2)
|
|
159
|
-
effectiveEmphasisLength -= matchLength;
|
|
160
|
-
} else {
|
|
161
|
-
// Upgrade to internal run (1->3 or 2->3)
|
|
162
|
-
effectiveEmphasisLength += matchLength;
|
|
163
|
-
}
|
|
164
|
-
// Back up one character so RegExp has a chance to match the
|
|
165
|
-
// next marker (ex: "**star**_underscore_")
|
|
166
|
-
if (emphasisRe.lastIndex > 1) {
|
|
167
|
-
emphasisRe.lastIndex--;
|
|
168
|
-
}
|
|
169
|
-
} else if (emphasisRe.lastIndex > 1) {
|
|
170
|
-
// Back up one character so RegExp has a chance to match the
|
|
171
|
-
// mis-matched marker (ex: "*text_*")
|
|
172
|
-
emphasisRe.lastIndex--;
|
|
85
|
+
);
|
|
173
86
|
}
|
|
174
87
|
}
|
|
175
|
-
if (emphasisIndex !== -1) {
|
|
176
|
-
pendingError = pendingError ||
|
|
177
|
-
handleRunEnd(line, lineIndex, 0, null, line.length, inTable);
|
|
178
|
-
// Adjust for pending run on new line
|
|
179
|
-
emphasisIndex = 0;
|
|
180
|
-
emphasisLength = 0;
|
|
181
|
-
}
|
|
182
88
|
}
|
|
183
|
-
|
|
89
|
+
}
|
|
184
90
|
}
|
|
185
91
|
};
|