Prevent link highlight in markdown code blocks and spans (#140816)

* Prevent link highlight in markdown codeblocks (#139770)

* Handle inline codespan variants for markdown link provider (#139770)

* Refactor codespan detection in markdown link provider (#139770)
This commit is contained in:
Waqas Ali
2022-01-26 04:38:50 +08:00
committed by GitHub
parent 283f478fc3
commit a514979b02
4 changed files with 153 additions and 39 deletions

View File

@@ -7,15 +7,16 @@ import * as assert from 'assert';
import 'mocha';
import * as vscode from 'vscode';
import LinkProvider from '../features/documentLinkProvider';
import { createNewMarkdownEngine } from './engine';
import { InMemoryDocument } from './inMemoryDocument';
import { noopToken } from './util';
import { joinLines, noopToken } from './util';
const testFile = vscode.Uri.joinPath(vscode.workspace.workspaceFolders![0].uri, 'x.md');
function getLinksForFile(fileContents: string) {
const doc = new InMemoryDocument(testFile, fileContents);
const provider = new LinkProvider();
const provider = new LinkProvider(createNewMarkdownEngine());
return provider.provideDocumentLinks(doc, noopToken);
}
@@ -27,63 +28,63 @@ function assertRangeEqual(expected: vscode.Range, actual: vscode.Range) {
}
suite('markdown.DocumentLinkProvider', () => {
test('Should not return anything for empty document', () => {
const links = getLinksForFile('');
test('Should not return anything for empty document', async () => {
const links = await getLinksForFile('');
assert.strictEqual(links.length, 0);
});
test('Should not return anything for simple document without links', () => {
const links = getLinksForFile('# a\nfdasfdfsafsa');
test('Should not return anything for simple document without links', async () => {
const links = await getLinksForFile('# a\nfdasfdfsafsa');
assert.strictEqual(links.length, 0);
});
test('Should detect basic http links', () => {
const links = getLinksForFile('a [b](https://example.com) c');
test('Should detect basic http links', async () => {
const links = await getLinksForFile('a [b](https://example.com) c');
assert.strictEqual(links.length, 1);
const [link] = links;
assertRangeEqual(link.range, new vscode.Range(0, 6, 0, 25));
});
test('Should detect basic workspace links', () => {
test('Should detect basic workspace links', async () => {
{
const links = getLinksForFile('a [b](./file) c');
const links = await getLinksForFile('a [b](./file) c');
assert.strictEqual(links.length, 1);
const [link] = links;
assertRangeEqual(link.range, new vscode.Range(0, 6, 0, 12));
}
{
const links = getLinksForFile('a [b](file.png) c');
const links = await getLinksForFile('a [b](file.png) c');
assert.strictEqual(links.length, 1);
const [link] = links;
assertRangeEqual(link.range, new vscode.Range(0, 6, 0, 14));
}
});
test('Should detect links with title', () => {
const links = getLinksForFile('a [b](https://example.com "abc") c');
test('Should detect links with title', async () => {
const links = await getLinksForFile('a [b](https://example.com "abc") c');
assert.strictEqual(links.length, 1);
const [link] = links;
assertRangeEqual(link.range, new vscode.Range(0, 6, 0, 25));
});
// #35245
test('Should handle links with escaped characters in name', () => {
const links = getLinksForFile('a [b\\]](./file)');
test('Should handle links with escaped characters in name', async () => {
const links = await getLinksForFile('a [b\\]](./file)');
assert.strictEqual(links.length, 1);
const [link] = links;
assertRangeEqual(link.range, new vscode.Range(0, 8, 0, 14));
});
test('Should handle links with balanced parens', () => {
test('Should handle links with balanced parens', async () => {
{
const links = getLinksForFile('a [b](https://example.com/a()c) c');
const links = await getLinksForFile('a [b](https://example.com/a()c) c');
assert.strictEqual(links.length, 1);
const [link] = links;
assertRangeEqual(link.range, new vscode.Range(0, 6, 0, 30));
}
{
const links = getLinksForFile('a [b](https://example.com/a(b)c) c');
const links = await getLinksForFile('a [b](https://example.com/a(b)c) c');
assert.strictEqual(links.length, 1);
const [link] = links;
assertRangeEqual(link.range, new vscode.Range(0, 6, 0, 31));
@@ -91,15 +92,15 @@ suite('markdown.DocumentLinkProvider', () => {
}
{
// #49011
const links = getLinksForFile('[A link](http://ThisUrlhasParens/A_link(in_parens))');
const links = await getLinksForFile('[A link](http://ThisUrlhasParens/A_link(in_parens))');
assert.strictEqual(links.length, 1);
const [link] = links;
assertRangeEqual(link.range, new vscode.Range(0, 9, 0, 50));
}
});
test('Should handle two links without space', () => {
const links = getLinksForFile('a ([test](test)[test2](test2)) c');
test('Should handle two links without space', async () => {
const links = await getLinksForFile('a ([test](test)[test2](test2)) c');
assert.strictEqual(links.length, 2);
const [link1, link2] = links;
assertRangeEqual(link1.range, new vscode.Range(0, 10, 0, 14));
@@ -107,23 +108,23 @@ suite('markdown.DocumentLinkProvider', () => {
});
// #49238
test('should handle hyperlinked images', () => {
test('should handle hyperlinked images', async () => {
{
const links = getLinksForFile('[![alt text](image.jpg)](https://example.com)');
const links = await getLinksForFile('[![alt text](image.jpg)](https://example.com)');
assert.strictEqual(links.length, 2);
const [link1, link2] = links;
assertRangeEqual(link1.range, new vscode.Range(0, 13, 0, 22));
assertRangeEqual(link2.range, new vscode.Range(0, 25, 0, 44));
}
{
const links = getLinksForFile('[![a]( whitespace.jpg )]( https://whitespace.com )');
const links = await getLinksForFile('[![a]( whitespace.jpg )]( https://whitespace.com )');
assert.strictEqual(links.length, 2);
const [link1, link2] = links;
assertRangeEqual(link1.range, new vscode.Range(0, 7, 0, 21));
assertRangeEqual(link2.range, new vscode.Range(0, 26, 0, 48));
}
{
const links = getLinksForFile('[![a](img1.jpg)](file1.txt) text [![a](img2.jpg)](file2.txt)');
const links = await getLinksForFile('[![a](img1.jpg)](file1.txt) text [![a](img2.jpg)](file2.txt)');
assert.strictEqual(links.length, 4);
const [link1, link2, link3, link4] = links;
assertRangeEqual(link1.range, new vscode.Range(0, 6, 0, 14));
@@ -133,13 +134,13 @@ suite('markdown.DocumentLinkProvider', () => {
}
});
test('Should not consider link references starting with ^ character valid (#107471)', () => {
const links = getLinksForFile('[^reference]: https://example.com');
test('Should not consider link references starting with ^ character valid (#107471)', async () => {
const links = await getLinksForFile('[^reference]: https://example.com');
assert.strictEqual(links.length, 0);
});
test('Should find definitions links with spaces in angle brackets (#136073)', () => {
const links = getLinksForFile([
test('Should find definitions links with spaces in angle brackets (#136073)', async () => {
const links = await getLinksForFile([
'[a]: <b c>',
'[b]: <cd>',
].join('\n'));
@@ -149,6 +150,75 @@ suite('markdown.DocumentLinkProvider', () => {
assertRangeEqual(link1.range, new vscode.Range(0, 6, 0, 9));
assertRangeEqual(link2.range, new vscode.Range(1, 6, 1, 8));
});
test('Should not consider links in code fenced with backticks', async () => {
const text = joinLines(
'```',
'[b](https://example.com)',
'```');
const links = await getLinksForFile(text);
assert.strictEqual(links.length, 0);
});
test('Should not consider links in code fenced with tilda', async () => {
const text = joinLines(
'~~~',
'[b](https://example.com)',
'~~~');
const links = await getLinksForFile(text);
assert.strictEqual(links.length, 0);
});
test('Should not consider links in indented code', async () => {
const links = await getLinksForFile(' [b](https://example.com)');
assert.strictEqual(links.length, 0);
});
test('Should not consider links in inline code span', async () => {
const links = await getLinksForFile('`[b](https://example.com)`');
assert.strictEqual(links.length, 0);
});
test('Should not consider links with code span inside', async () => {
const links = await getLinksForFile('[li`nk](https://example.com`)');
assert.strictEqual(links.length, 0);
});
test('Should not consider links in multiline inline code span', async () => {
const text = joinLines(
'`` ',
'[b](https://example.com)',
'``');
const links = await getLinksForFile(text);
assert.strictEqual(links.length, 0);
});
test('Should not consider links in multiline inline code span between between text', async () => {
const text = joinLines(
'[b](https://1.com) `[b](https://2.com)',
'` [b](https://3.com)');
const links = await getLinksForFile(text);
assert.deepStrictEqual(links.map(l => l.target?.authority), ['1.com', '3.com'])
});
test('Should not consider links in multiline inline code span with new line after the first backtick', async () => {
const text = joinLines(
'`',
'[b](https://example.com)`');
const links = await getLinksForFile(text);
assert.strictEqual(links.length, 0);
});
test('Should not miss links in invalid multiline inline code span', async () => {
const text = joinLines(
'`` ',
'',
'[b](https://example.com)',
'',
'``');
const links = await getLinksForFile(text);
assert.strictEqual(links.length, 1);
});
});