Fix markdown link detection for links with titles (#151459)

Fixes #151458
This commit is contained in:
Matt Bierner 2022-06-07 13:58:29 -07:00 committed by GitHub
parent 677d79a4cd
commit 9545af80f6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 62 additions and 13 deletions

View file

@ -185,12 +185,12 @@ function stripAngleBrackets(link: string) {
/**
* Matches `[text](link)`
*/
const linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]])*\])\(\s*)(([^\s\(\)]|\([^\s\(\)]*?\))+)\s*(".*?")?\)/g;
const linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]])*\])\(\s*)(([^\s\(\)]|\([^\s\(\)]*?\))+)\s*("[^"]*"|'[^']*'|\([^\(\)]*\))?\s*\)/g;
/**
* Matches `[text](<link>)`
*/
const linkPatternAngle = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]])*\])\(\s*<)(([^<>]|\([^\s\(\)]*?\))+)>\s*(".*?")?\)/g;
const linkPatternAngle = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]])*\])\(\s*<)(([^<>]|\([^\s\(\)]*?\))+)>\s*("[^"]*"|'[^']*'|\([^\(\)]*\))?\s*\)/g;
/**

View file

@ -37,6 +37,14 @@ function createDiagnosticsManager(workspaceContents: MdWorkspaceContents, config
return new DiagnosticManager(new DiagnosticComputer(engine, workspaceContents, linkProvider), configuration);
}
function assertDiagnosticsEqual(actual: readonly vscode.Diagnostic[], expectedRanges: readonly vscode.Range[]) {
assert.strictEqual(actual.length, expectedRanges.length);
for (let i = 0; i < actual.length; ++i) {
assertRangeEqual(actual[i].range, expectedRanges[i], `Range ${i} to be equal`);
}
}
class MemoryDiagnosticConfiguration implements DiagnosticConfiguration {
private readonly _onDidChange = new vscode.EventEmitter<void>();
@ -87,9 +95,10 @@ suite('markdown: Diagnostics', () => {
));
const diagnostics = await getComputedDiagnostics(doc, new InMemoryWorkspaceMarkdownDocuments([doc]));
assert.deepStrictEqual(diagnostics.length, 2);
assertRangeEqual(new vscode.Range(0, 6, 0, 22), diagnostics[0].range);
assertRangeEqual(new vscode.Range(3, 11, 3, 27), diagnostics[1].range);
assertDiagnosticsEqual(diagnostics, [
new vscode.Range(0, 6, 0, 22),
new vscode.Range(3, 11, 3, 27),
]);
});
test('Should generate diagnostics for links to header that does not exist in current file', async () => {
@ -103,9 +112,10 @@ suite('markdown: Diagnostics', () => {
));
const diagnostics = await getComputedDiagnostics(doc, new InMemoryWorkspaceMarkdownDocuments([doc]));
assert.deepStrictEqual(diagnostics.length, 2);
assertRangeEqual(new vscode.Range(2, 6, 2, 21), diagnostics[0].range);
assertRangeEqual(new vscode.Range(5, 11, 5, 26), diagnostics[1].range);
assertDiagnosticsEqual(diagnostics, [
new vscode.Range(2, 6, 2, 21),
new vscode.Range(5, 11, 5, 26),
]);
});
test('Should generate diagnostics for links to non-existent headers in other files', async () => {
@ -123,8 +133,9 @@ suite('markdown: Diagnostics', () => {
));
const diagnostics = await getComputedDiagnostics(doc1, new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]));
assert.deepStrictEqual(diagnostics.length, 1);
assertRangeEqual(new vscode.Range(5, 6, 5, 35), diagnostics[0].range);
assertDiagnosticsEqual(diagnostics, [
new vscode.Range(5, 6, 5, 35),
]);
});
test('Should support links both with and without .md file extension', async () => {
@ -150,8 +161,9 @@ suite('markdown: Diagnostics', () => {
));
const diagnostics = await getComputedDiagnostics(doc, new InMemoryWorkspaceMarkdownDocuments([doc]));
assert.deepStrictEqual(diagnostics.length, 1);
assertRangeEqual(new vscode.Range(1, 11, 1, 18), diagnostics[0].range);
assertDiagnosticsEqual(diagnostics, [
new vscode.Range(1, 11, 1, 18),
]);
});
test('Should not generate diagnostics when validate is disabled', async () => {
@ -281,4 +293,24 @@ suite('markdown: Diagnostics', () => {
const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken);
assert.deepStrictEqual(diagnostics.length, 0);
});
test('Should detect invalid links with titles', async () => {
const doc = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
`[link](<no such.md> "text")`,
`[link](<no such.md> 'text')`,
`[link](<no such.md> (text))`,
`[link](no-such.md "text")`,
`[link](no-such.md 'text')`,
`[link](no-such.md (text))`,
));
const diagnostics = await getComputedDiagnostics(doc, new InMemoryWorkspaceMarkdownDocuments([doc]));
assertDiagnosticsEqual(diagnostics, [
new vscode.Range(0, 8, 0, 18),
new vscode.Range(1, 8, 1, 18),
new vscode.Range(2, 8, 2, 18),
new vscode.Range(3, 7, 3, 17),
new vscode.Range(4, 7, 4, 17),
new vscode.Range(5, 7, 5, 17),
]);
});
});

View file

@ -408,5 +408,22 @@ suite('markdown.DocumentLinkProvider', () => {
assertLinksEqual(links, [new vscode.Range(0, 8, 0, 13)]);
});
test('Should find links with titles', async () => {
const links = await getLinksForFile(joinLines(
`[link](<no such.md> "text")`,
`[link](<no such.md> 'text')`,
`[link](<no such.md> (text))`,
`[link](no-such.md "text")`,
`[link](no-such.md 'text')`,
`[link](no-such.md (text))`,
));
assertLinksEqual(links, [
new vscode.Range(0, 8, 0, 18),
new vscode.Range(1, 8, 1, 18),
new vscode.Range(2, 8, 2, 18),
new vscode.Range(3, 7, 3, 17),
new vscode.Range(4, 7, 4, 17),
new vscode.Range(5, 7, 5, 17),
]);
});
});