From d372fa0697992a4bc3b7af8b07737e17cea2d383 Mon Sep 17 00:00:00 2001 From: Phil Marshall Date: Mon, 21 Jan 2019 23:25:05 -0600 Subject: [PATCH 1/7] md extension should underline images used as link description --- .../src/features/documentLinkProvider.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/extensions/markdown-language-features/src/features/documentLinkProvider.ts b/extensions/markdown-language-features/src/features/documentLinkProvider.ts index f4e1bd0a9d2..ded01382275 100644 --- a/extensions/markdown-language-features/src/features/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/features/documentLinkProvider.ts @@ -51,7 +51,7 @@ function matchAll( } export default class LinkProvider implements vscode.DocumentLinkProvider { - private readonly linkPattern = /(\[[^\]]*\]\(\s*)((([^\s\(\)]|\(\S*?\))+))\s*(".*?")?\)/g; + private readonly linkPattern = /(\[((!\[(.+)\]\()(.+)\)\]|[^\]]*\])\(\s*)((([^\s\(\)]|\(\S*?\))+))\s*(".*?")?\)/g; private readonly referenceLinkPattern = /(\[([^\]]+)\]\[\s*?)([^\s\]]*?)\]/g; private readonly definitionPattern = /^([\t ]*\[([^\]]+)\]:\s*)(\S+)/gm; @@ -74,7 +74,7 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { const results: vscode.DocumentLink[] = []; for (const match of matchAll(this.linkPattern, text)) { const pre = match[1]; - const link = match[2]; + const link = match[6]; const offset = (match.index || 0) + pre.length; const linkStart = document.positionAt(offset); const linkEnd = document.positionAt(offset + link.length); @@ -85,6 +85,20 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { } catch (e) { // noop } + if (match[5]) { + const imagePre = match[3]; + const imageLink = match[5]; + const imageOffset = (match.index || 0) + imagePre.length + 1; + const imageLinkStart = document.positionAt(imageOffset); + const imageLinkEnd = document.positionAt(imageOffset + imageLink.length); + try { + results.push(new vscode.DocumentLink( + new vscode.Range(imageLinkStart, imageLinkEnd), + normalizeLink(document, imageLink, base))); + } catch (e) { + // noop + } + } } return results; From b1de159292ff8ad57aa2190212abfda25e95f51e Mon Sep 17 00:00:00 2001 From: Phil Marshall Date: Tue, 22 Jan 2019 18:36:45 -0600 Subject: [PATCH 2/7] added test for markdown image link underline --- .../src/test/documentLinkProvider.test.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts index 5a1d0c87880..1d94141d76e 100644 --- a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts +++ b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts @@ -103,6 +103,14 @@ suite('markdown.DocumentLinkProvider', () => { assertRangeEqual(link1.range, new vscode.Range(0, 10, 0, 14)); assertRangeEqual(link2.range, new vscode.Range(0, 23, 0, 28)); }); + + test('should handle hyperlinked images', () => { + const links = getLinksForFile('[![alt text](image.jpg)](https://example.com)'); + assert.strictEqual(links.length, 2); + const [link1, link2] = links; + assertRangeEqual(link1.range, new vscode.Range(0,25,0,44)); + assertRangeEqual(link2.range, new vscode.Range(0,13,0,22)); + }); }); From 5fdc5c2675c11f31cb9bef972ea3de4b5bfaad86 Mon Sep 17 00:00:00 2001 From: Phil Marshall Date: Thu, 24 Jan 2019 17:25:09 -0600 Subject: [PATCH 3/7] factor documentlink extraction into separate function --- .../src/features/documentLinkProvider.ts | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/extensions/markdown-language-features/src/features/documentLinkProvider.ts b/extensions/markdown-language-features/src/features/documentLinkProvider.ts index ded01382275..ec07f8339b5 100644 --- a/extensions/markdown-language-features/src/features/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/features/documentLinkProvider.ts @@ -50,6 +50,25 @@ function matchAll( return out; } +function extractDocumentLink( + document: vscode.TextDocument, + base: string, + pre: number, + link: string, + matchIndex: number | undefined +): vscode.DocumentLink | undefined { + const offset = (matchIndex || 0) + pre; + const linkStart = document.positionAt(offset); + const linkEnd = document.positionAt(offset + link.length); + try { + return new vscode.DocumentLink( + new vscode.Range(linkStart, linkEnd), + normalizeLink(document, link, base)); + } catch (e) { + return undefined; + } +} + export default class LinkProvider implements vscode.DocumentLinkProvider { private readonly linkPattern = /(\[((!\[(.+)\]\()(.+)\)\]|[^\]]*\])\(\s*)((([^\s\(\)]|\(\S*?\))+))\s*(".*?")?\)/g; private readonly referenceLinkPattern = /(\[([^\]]+)\]\[\s*?)([^\s\]]*?)\]/g; @@ -73,34 +92,15 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { ): vscode.DocumentLink[] { const results: vscode.DocumentLink[] = []; for (const match of matchAll(this.linkPattern, text)) { - const pre = match[1]; - const link = match[6]; - const offset = (match.index || 0) + pre.length; - const linkStart = document.positionAt(offset); - const linkEnd = document.positionAt(offset + link.length); - try { - results.push(new vscode.DocumentLink( - new vscode.Range(linkStart, linkEnd), - normalizeLink(document, link, base))); - } catch (e) { - // noop + const urlLink = extractDocumentLink(document, base, match[1].length, match[6], match.index); + if (urlLink) { + results.push(urlLink); } - if (match[5]) { - const imagePre = match[3]; - const imageLink = match[5]; - const imageOffset = (match.index || 0) + imagePre.length + 1; - const imageLinkStart = document.positionAt(imageOffset); - const imageLinkEnd = document.positionAt(imageOffset + imageLink.length); - try { - results.push(new vscode.DocumentLink( - new vscode.Range(imageLinkStart, imageLinkEnd), - normalizeLink(document, imageLink, base))); - } catch (e) { - // noop - } + const imgLink = match[5] && extractDocumentLink(document, base, match[3].length + 1, match[5], match.index); + if (imgLink) { + results.push(imgLink); } } - return results; } @@ -173,4 +173,4 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { } return out; } -} +} \ No newline at end of file From 9070abedaf002ff4bc4998985bed89e57ef415f2 Mon Sep 17 00:00:00 2001 From: Phil Marshall Date: Fri, 25 Jan 2019 18:45:06 -0600 Subject: [PATCH 4/7] image link should be pushed to results before other link --- .../src/features/documentLinkProvider.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/markdown-language-features/src/features/documentLinkProvider.ts b/extensions/markdown-language-features/src/features/documentLinkProvider.ts index ec07f8339b5..fe4eeaa6ebc 100644 --- a/extensions/markdown-language-features/src/features/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/features/documentLinkProvider.ts @@ -92,13 +92,13 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { ): vscode.DocumentLink[] { const results: vscode.DocumentLink[] = []; for (const match of matchAll(this.linkPattern, text)) { - const urlLink = extractDocumentLink(document, base, match[1].length, match[6], match.index); - if (urlLink) { - results.push(urlLink); + const matchImage = match[5] && extractDocumentLink(document, base, match[3].length + 1, match[5], match.index); + if (matchImage) { + results.push(matchImage); } - const imgLink = match[5] && extractDocumentLink(document, base, match[3].length + 1, match[5], match.index); - if (imgLink) { - results.push(imgLink); + const matchLink = extractDocumentLink(document, base, match[1].length, match[6], match.index); + if (matchLink) { + results.push(matchLink); } } return results; From a7c88d79a2ac768930d40f9a8f65ae91a6a485c7 Mon Sep 17 00:00:00 2001 From: Phil Marshall Date: Fri, 25 Jan 2019 18:46:28 -0600 Subject: [PATCH 5/7] updated test for switched order and new tests for image link --- .../src/test/documentLinkProvider.test.ts | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts index 1d94141d76e..f6309a027d3 100644 --- a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts +++ b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts @@ -104,12 +104,31 @@ suite('markdown.DocumentLinkProvider', () => { assertRangeEqual(link2.range, new vscode.Range(0, 23, 0, 28)); }); + // #49238 test('should handle hyperlinked images', () => { - const links = getLinksForFile('[![alt text](image.jpg)](https://example.com)'); - assert.strictEqual(links.length, 2); - const [link1, link2] = links; - assertRangeEqual(link1.range, new vscode.Range(0,25,0,44)); - assertRangeEqual(link2.range, new vscode.Range(0,13,0,22)); + { + const links = 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 )'); + 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)'); + assert.strictEqual(links.length, 4); + const [link1, link2, link3, link4] = links; + assertRangeEqual(link1.range, new vscode.Range(0,6,0,14)); + assertRangeEqual(link2.range, new vscode.Range(0,17,0,26)); + assertRangeEqual(link3.range, new vscode.Range(0,39,0,47)); + assertRangeEqual(link4.range, new vscode.Range(0,50,0,59)); + } }); }); From ef66e5fab1398f7c4387bcc8897ff5faf7268d80 Mon Sep 17 00:00:00 2001 From: Phil Marshall Date: Fri, 25 Jan 2019 18:55:42 -0600 Subject: [PATCH 6/7] tweaked linkPattern regex to pass new tests --- .../src/features/documentLinkProvider.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/markdown-language-features/src/features/documentLinkProvider.ts b/extensions/markdown-language-features/src/features/documentLinkProvider.ts index fe4eeaa6ebc..5d6fdbd926c 100644 --- a/extensions/markdown-language-features/src/features/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/features/documentLinkProvider.ts @@ -70,7 +70,7 @@ function extractDocumentLink( } export default class LinkProvider implements vscode.DocumentLinkProvider { - private readonly linkPattern = /(\[((!\[(.+)\]\()(.+)\)\]|[^\]]*\])\(\s*)((([^\s\(\)]|\(\S*?\))+))\s*(".*?")?\)/g; + private readonly linkPattern = /(\[((!\[[^\]]*\]\(\s*)([^\s\(\)]+)\s*\)\]|[^\]]*\])\(\s*)(([^\s\(\)]|\(\S*?\))+)\s*(".*?")?\)/g; private readonly referenceLinkPattern = /(\[([^\]]+)\]\[\s*?)([^\s\]]*?)\]/g; private readonly definitionPattern = /^([\t ]*\[([^\]]+)\]:\s*)(\S+)/gm; @@ -92,11 +92,11 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { ): vscode.DocumentLink[] { const results: vscode.DocumentLink[] = []; for (const match of matchAll(this.linkPattern, text)) { - const matchImage = match[5] && extractDocumentLink(document, base, match[3].length + 1, match[5], match.index); + const matchImage = match[4] && extractDocumentLink(document, base, match[3].length + 1, match[4], match.index); if (matchImage) { results.push(matchImage); } - const matchLink = extractDocumentLink(document, base, match[1].length, match[6], match.index); + const matchLink = extractDocumentLink(document, base, match[1].length, match[5], match.index); if (matchLink) { results.push(matchLink); } From 4c2d33559f3b64248b2564aea21449398e8fe3ed Mon Sep 17 00:00:00 2001 From: Phil Marshall Date: Fri, 25 Jan 2019 19:07:45 -0600 Subject: [PATCH 7/7] lazy quantifiers for linkPattern regex --- .../src/features/documentLinkProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/src/features/documentLinkProvider.ts b/extensions/markdown-language-features/src/features/documentLinkProvider.ts index 5d6fdbd926c..b35f3f0d787 100644 --- a/extensions/markdown-language-features/src/features/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/features/documentLinkProvider.ts @@ -70,7 +70,7 @@ function extractDocumentLink( } export default class LinkProvider implements vscode.DocumentLinkProvider { - private readonly linkPattern = /(\[((!\[[^\]]*\]\(\s*)([^\s\(\)]+)\s*\)\]|[^\]]*\])\(\s*)(([^\s\(\)]|\(\S*?\))+)\s*(".*?")?\)/g; + private readonly linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|[^\]]*\])\(\s*)(([^\s\(\)]|\(\S*?\))+)\s*(".*?")?\)/g; private readonly referenceLinkPattern = /(\[([^\]]+)\]\[\s*?)([^\s\]]*?)\]/g; private readonly definitionPattern = /^([\t ]*\[([^\]]+)\]:\s*)(\S+)/gm;