Skip to content

Commit

Permalink
Fix bugs in no-invalid-label-refs
Browse files Browse the repository at this point in the history
  • Loading branch information
nzakas committed Aug 16, 2024
1 parent 48cfd9a commit 548fdab
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 35 deletions.
70 changes: 48 additions & 22 deletions src/rules/no-invalid-label-refs.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,24 @@ const labelPattern = /\]\[([^\]]+)\]/u;
/**
* Finds missing references in a node.
* @param {TextNode} node The node to check.
* @param {string} text The text of the node.
* @param {string} docText The text of the node.
* @returns {Array<{label:string,position:Position}>} The missing references.
*/
function findInvalidLabelReferences(node, text) {
function findInvalidLabelReferences(node, docText) {

const invalid = [];
let startIndex = 0;

const offset = node.position.start.offset;
const nodeStartLine = node.position.start.line;
const nodeStartColumn = node.position.start.column;

/*
* This loop works by searching the string inside the node for the next
* label reference. If it finds one, it checks to see if there is any
* white space between the [ and ]. If there is, it reports an error.
* It then moves the start index to the end of the label reference and
* continues searching the text until the end of the text is found.
*/
while (startIndex < node.value.length) {

const value = node.value.slice(startIndex);
Expand All @@ -46,45 +56,61 @@ function findInvalidLabelReferences(node, text) {
}

if (!illegalShorthandTailPattern.test(match[0])) {
startIndex += match.index + match[0].length;
continue;
}

let columnStart = startIndex + match.index + 1;
/*
* Calculate the match index relative to just the node and
* to the entire document text.
*/
const nodeMatchIndex = startIndex + match.index;
const docMatchIndex = offset + nodeMatchIndex;

// adding 1 to the index just in case we're in a ![] and need to skip the !.
const startFrom = node.position.start.offset + startIndex + 1;
const lastOpenBracket = text.lastIndexOf("[", startFrom);
/*
* Search the entire document text to find the preceding open bracket.
* We add one to the start index to account for a preceding ! in an image.
*/
const lastOpenBracketIndex = docText.lastIndexOf("[", docMatchIndex);

if (lastOpenBracket === -1) {
if (lastOpenBracketIndex === -1) {
startIndex += match.index + match[0].length;
continue;
}

const label = text.slice(lastOpenBracket, columnStart + match[0].length).match(/!?\[([^\]]+)\]/u)?.[1];
/*
* Note: `label` can contain leading and trailing newlines, so we need to
* take that into account when calculating the line and column offsets.
*/
const label = docText.slice(lastOpenBracketIndex, docMatchIndex + match[0].length).match(/!?\[([^\]]+)\]/u)[1];

columnStart -= label.length;
// find location of [ in the document text
const {
lineOffset: startLineOffset,
columnOffset: startColumnOffset
} = findOffsets(node.value, nodeMatchIndex + 1);

// find location of [ in the document text
const {
lineOffset,
columnOffset
} = findOffsets(node.value, columnStart);
lineOffset: endLineOffset,
columnOffset: endColumnOffset
} = findOffsets(node.value, nodeMatchIndex + match[0].length);

const line = node.position.start.line + lineOffset;
const startLine = nodeStartLine + startLineOffset;
const startColumn = nodeStartColumn + startColumnOffset;
const endLine = nodeStartLine + endLineOffset;
const endColumn = (endLine === startLine ? nodeStartColumn : 0) + endColumnOffset;

/*
* If the columnOffset is 0, then the column is at the start of the line.
* In that case, we need to adjust the column number to be 1.
*/
invalid.push({
label: label.trim(),
position: {
start: {
line,
column: columnOffset || 1
line: startLine,
column: startColumn
},
end: {
line,
column: columnOffset + label.length
line: endLine,
column: endColumn
}
}
});
Expand Down
8 changes: 4 additions & 4 deletions src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@
export const illegalShorthandTailPattern = /\]\[\s+\]$/u;

/**
* Finds the line and column offsets for a given start offset in a string.
* Finds the line and column offsets for a given offset in a string.
* @param {string} text The text to search.
* @param {number} startOffset The offset to find.
* @param {number} offset The offset to find.
* @returns {{lineOffset:number,columnOffset:number}} The location of the offset.
*/
export function findOffsets(text, startOffset) {
export function findOffsets(text, offset) {

let lineOffset = 0;
let columnOffset = 0;

for (let i = 0; i < startOffset; i++) {
for (let i = 0; i < offset; i++) {
if (text[i] === "\n") {
lineOffset++;
columnOffset = 0;
Expand Down
65 changes: 56 additions & 9 deletions tests/rules/no-invalid-label-refs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,22 @@ ruleTester.run("no-invalid-label-refs", rule, {
messageId: "invalidLabelRef",
data: { label: "foo" },
line: 1,
column: 2,
column: 6,
endLine: 1,
endColumn: 9
}
]
},
{
code: "![foo][ ]\n\n[foo]: http://bar.com/image.jpg",
errors: [
{
messageId: "invalidLabelRef",
data: { label: "foo" },
line: 1,
column: 7,
endLine: 1,
endColumn: 5
endColumn: 10
}
]
},
Expand All @@ -52,33 +65,67 @@ ruleTester.run("no-invalid-label-refs", rule, {
{
messageId: "invalidLabelRef",
data: { label: "foo" },
line: 3,
column: 2,
endLine: 4,
endColumn: 1
}
]
},
{
code: "[foo][ ]\n[bar][ ]\n\n[foo]: http://foo.com\n[bar]: http://bar.com",
errors: [
{
messageId: "invalidLabelRef",
data: { label: "foo" },
line: 1,
column: 6,
endLine: 1,
endColumn: 9
},
{
messageId: "invalidLabelRef",
data: { label: "bar" },
line: 2,
column: 1,
column: 6,
endLine: 2,
endColumn: 5
endColumn: 9
}
]
},
{
code: "[foo][ ]\n[bar][ ]\n\n[foo]: http://foo.com\n[bar]: http://bar.com",
code: "[foo][ ]\n![bar][ ]\n\n[foo]: http://foo.com\n[bar]: http://bar.com",
errors: [
{
messageId: "invalidLabelRef",
data: { label: "foo" },
line: 1,
column: 2,
column: 6,
endLine: 1,
endColumn: 5
endColumn: 9
},
{
messageId: "invalidLabelRef",
data: { label: "bar" },
line: 2,
column: 2,
column: 7,
endLine: 2,
endColumn: 5
endColumn: 10
}
]
},
{
code: "- - - [foo][ ]\n\n[foo]: http://foo.com",
errors: [
{
messageId: "invalidLabelRef",
data: { label: "foo" },
line: 1,
column: 12,
endLine: 1,
endColumn: 15
}
]
}
]
});

0 comments on commit 548fdab

Please sign in to comment.