Skip to content

Commit

Permalink
Update MD053/link-image-reference-definitions to recognize links with…
Browse files Browse the repository at this point in the history
…in square brackets (fixes #537).
  • Loading branch information
DavidAnson committed Jul 30, 2022
1 parent 08cdd95 commit 48f47b5
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 69 deletions.
55 changes: 27 additions & 28 deletions demo/markdownlint-browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ module.exports.listItemMarkerRe = /^([\s>]*)(?:[*+-]|\d+[.)])\s+/;
module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/;
// Regular expression for all instances of emphasis markers
const emphasisMarkersRe = /[_*]/g;
// Regular expression for reference links (full and collapsed but not shortcut)
const referenceLinkRe = /!?\\?\[((?:\[[^\]\0]*]|[^\]\0])*)](?:(?:\[([^\]\0]*)\])|[^(]|$)/g;
// Regular expression for reference links (full, collapsed, and shortcut)
const referenceLinkRe = /!?\\?\[((?:\[[^\]\0]*]|[^\]\0])*)](?:(?:\[([^\]\0]*)\])|([^(])|$)/g;
// Regular expression for link reference definitions
const linkReferenceDefinitionRe = /^ {0,3}\[([^\]]*[^\\])]:/;
module.exports.linkReferenceDefinitionRe = linkReferenceDefinitionRe;
Expand Down Expand Up @@ -755,7 +755,7 @@ function getReferenceLinkImageData(lineMetadata) {
// Define helper functions
const normalizeLabel = (s) => s.toLowerCase().trim().replace(/\s+/g, " ");
const exclusions = [];
const excluded = (match) => withinAnyRange(exclusions, 0, match.index, match[0].length);
const excluded = (match) => withinAnyRange(exclusions, 0, match.index, match[0].length - (match[3] || "").length);
// Convert input to single-line so multi-line links/images are easier
const lineOffsets = [];
let currentOffset = 0;
Expand Down Expand Up @@ -813,30 +813,29 @@ function getReferenceLinkImageData(lineMetadata) {
!matchString.startsWith("!\\") &&
!matchText.endsWith("\\") &&
!(matchLabel || "").endsWith("\\") &&
(topLevel || matchString.startsWith("!")) &&
!excluded(referenceLinkMatch)) {
!(topLevel && excluded(referenceLinkMatch))) {
const shortcutLink = (matchLabel === undefined);
const collapsedLink = (!shortcutLink && (matchLabel.length === 0));
const label = normalizeLabel((shortcutLink || collapsedLink) ? matchText : matchLabel);
if (label.length > 0) {
const referenceindex = referenceLinkMatch.index;
if (topLevel) {
// Calculate line index
while (lineOffsets[lineIndex + 1] <= referenceindex) {
lineIndex++;
}
}
else {
// Use provided line index
lineIndex = contentLineIndex;
}
const referenceIndex = referenceindex +
(topLevel ? -lineOffsets[lineIndex] : contentIndex);
if (shortcutLink) {
// Track, but don't validate due to ambiguity: "text [text] text"
// Track separately due to ambiguity in "text [text] text"
shortcuts.add(label);
}
else {
const referenceindex = referenceLinkMatch.index;
if (topLevel) {
// Calculate line index
while (lineOffsets[lineIndex + 1] <= referenceindex) {
lineIndex++;
}
}
else {
// Use provided line index
lineIndex = contentLineIndex;
}
const referenceIndex = referenceindex +
(topLevel ? -lineOffsets[lineIndex] : contentIndex);
// Track reference and location
const referenceData = references.get(label) || [];
referenceData.push([
Expand All @@ -845,15 +844,15 @@ function getReferenceLinkImageData(lineMetadata) {
matchString.length
]);
references.set(label, referenceData);
// Check for images embedded in top-level link text
if (!matchString.startsWith("!")) {
pendingContents.push({
"content": matchText,
"contentLineIndex": lineIndex,
"contentIndex": referenceIndex + 1,
"topLevel": false
});
}
}
// Check for links embedded in brackets
if (!matchString.startsWith("!")) {
pendingContents.push({
"content": matchText,
"contentLineIndex": lineIndex,
"contentIndex": referenceIndex + 1,
"topLevel": false
});
}
}
}
Expand Down
55 changes: 26 additions & 29 deletions helpers/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/;
// Regular expression for all instances of emphasis markers
const emphasisMarkersRe = /[_*]/g;

// Regular expression for reference links (full and collapsed but not shortcut)
// Regular expression for reference links (full, collapsed, and shortcut)
const referenceLinkRe =
/!?\\?\[((?:\[[^\]\0]*]|[^\]\0])*)](?:(?:\[([^\]\0]*)\])|[^(]|$)/g;
/!?\\?\[((?:\[[^\]\0]*]|[^\]\0])*)](?:(?:\[([^\]\0]*)\])|([^(])|$)/g;

// Regular expression for link reference definitions
const linkReferenceDefinitionRe = /^ {0,3}\[([^\]]*[^\\])]:/;
Expand Down Expand Up @@ -785,7 +785,7 @@ function getReferenceLinkImageData(lineMetadata) {
const normalizeLabel = (s) => s.toLowerCase().trim().replace(/\s+/g, " ");
const exclusions = [];
const excluded = (match) => withinAnyRange(
exclusions, 0, match.index, match[0].length
exclusions, 0, match.index, match[0].length - (match[3] || "").length
);
// Convert input to single-line so multi-line links/images are easier
const lineOffsets = [];
Expand Down Expand Up @@ -845,8 +845,7 @@ function getReferenceLinkImageData(lineMetadata) {
!matchString.startsWith("!\\") &&
!matchText.endsWith("\\") &&
!(matchLabel || "").endsWith("\\") &&
(topLevel || matchString.startsWith("!")) &&
!excluded(referenceLinkMatch)
!(topLevel && excluded(referenceLinkMatch))
) {
const shortcutLink = (matchLabel === undefined);
const collapsedLink =
Expand All @@ -855,22 +854,22 @@ function getReferenceLinkImageData(lineMetadata) {
(shortcutLink || collapsedLink) ? matchText : matchLabel
);
if (label.length > 0) {
const referenceindex = referenceLinkMatch.index;
if (topLevel) {
// Calculate line index
while (lineOffsets[lineIndex + 1] <= referenceindex) {
lineIndex++;
}
} else {
// Use provided line index
lineIndex = contentLineIndex;
}
const referenceIndex = referenceindex +
(topLevel ? -lineOffsets[lineIndex] : contentIndex);
if (shortcutLink) {
// Track, but don't validate due to ambiguity: "text [text] text"
// Track separately due to ambiguity in "text [text] text"
shortcuts.add(label);
} else {
const referenceindex = referenceLinkMatch.index;
if (topLevel) {
// Calculate line index
while (lineOffsets[lineIndex + 1] <= referenceindex) {
lineIndex++;
}
} else {
// Use provided line index
lineIndex = contentLineIndex;
}
const referenceIndex = referenceindex +
(topLevel ? -lineOffsets[lineIndex] : contentIndex);
// Track reference and location
const referenceData = references.get(label) || [];
referenceData.push([
Expand All @@ -879,17 +878,15 @@ function getReferenceLinkImageData(lineMetadata) {
matchString.length
]);
references.set(label, referenceData);
// Check for images embedded in top-level link text
if (!matchString.startsWith("!")) {
pendingContents.push(
{
"content": matchText,
"contentLineIndex": lineIndex,
"contentIndex": referenceIndex + 1,
"topLevel": false
}
);
}
}
// Check for links embedded in brackets
if (!matchString.startsWith("!")) {
pendingContents.push({
"content": matchText,
"contentLineIndex": lineIndex,
"contentIndex": referenceIndex + 1,
"topLevel": false
});
}
}
}
Expand Down
26 changes: 26 additions & 0 deletions test/reference-links-and-images.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ Use of multi-line label: [multi-line-label][]

Standard link: [text](https://example.com/standard)

Wrapped in brackets: [[text][unique0]] [[unique1][]] [[unique2]]

[Embedded [text][unique3] in [unique4][] brackets [unique5]]

## Invalid Links

Missing label: [text][missing] {MD052}
Expand All @@ -61,8 +65,18 @@ Space: [text] [wrong]

Empty: [text][ ]

Code span: `[wrong]`

Code span: `[wrong][]`

Code span: `[text][wrong]`

Code span: `[[wrong]]`

Code span: `[[wrong][]]`

Code span: `[[text][wrong]]`

Escaped left text: \[text][wrong]

Escaped right text: [text\][wrong]
Expand All @@ -81,6 +95,10 @@ Shortcut style: ![image]

Image in link: [![text][image]][label] [![image][]][label] [![image]][label]

Wrapped in brackets: [![text][unique6]]

Embedded [in ![text][unique7] brackets]

## Invalid Images

Image only: ![text][missing] {MD052}
Expand Down Expand Up @@ -119,6 +137,14 @@ Missing[^2]
[colon]: https://example.com/colon
[multi-line-label]:
https://example.com/multi-line-label
[unique0]: https://example.com/unique0
[unique1]: https://example.com/unique1
[unique2]: https://example.com/unique2
[unique3]: https://example.com/unique3
[unique4]: https://example.com/unique4
[unique5]: https://example.com/unique5
[unique6]: https://example.com/unique6
[unique7]: https://example.com/unique7

## Invalid Labels

Expand Down
Loading

0 comments on commit 48f47b5

Please sign in to comment.