Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fix link with angle brackets around href #1851

Merged
merged 4 commits into from
Dec 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 35 additions & 14 deletions src/Tokenizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -457,34 +457,56 @@ module.exports = class Tokenizer {
link(src) {
const cap = this.rules.inline.link.exec(src);
if (cap) {
const lastParenIndex = findClosingBracket(cap[2], '()');
if (lastParenIndex > -1) {
const start = cap[0].indexOf('!') === 0 ? 5 : 4;
const linkLen = start + cap[1].length + lastParenIndex;
cap[2] = cap[2].substring(0, lastParenIndex);
cap[0] = cap[0].substring(0, linkLen).trim();
cap[3] = '';
const trimmedUrl = cap[2].trim();
if (!this.options.pedantic && trimmedUrl.startsWith('<')) {
// commonmark requires matching angle brackets
if (!trimmedUrl.endsWith('>')) {
return;
}

// ending angle bracket cannot be escaped
const rtrimSlash = rtrim(trimmedUrl.slice(0, -1), '\\');
if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) {
return;
}
calculuschild marked this conversation as resolved.
Show resolved Hide resolved
} else {
// find closing parenthesis
const lastParenIndex = findClosingBracket(cap[2], '()');
if (lastParenIndex > -1) {
const start = cap[0].indexOf('!') === 0 ? 5 : 4;
const linkLen = start + cap[1].length + lastParenIndex;
cap[2] = cap[2].substring(0, lastParenIndex);
cap[0] = cap[0].substring(0, linkLen).trim();
cap[3] = '';
}
}
let href = cap[2];
let title = '';
if (this.options.pedantic) {
// split pedantic href and title
const link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);

if (link) {
href = link[1];
title = link[3];
} else {
title = '';
}
} else {
title = cap[3] ? cap[3].slice(1, -1) : '';
}
href = href.trim().replace(/^<([\s\S]*)>$/, '$1');
const token = outputLink(cap, {

href = href.trim();
if (href.startsWith('<')) {
if (this.options.pedantic && !trimmedUrl.endsWith('>')) {
Copy link
Contributor

@calculuschild calculuschild Dec 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are multiple (I think 3?) checks for the pedantic option. Is there some way all of the pedantic logic can be handled together?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would require duplicating a lot of the logic since most of the logic is the same.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my PR on your branch. Broke out my old boolean logic notes and reordered some things around. Not sure if it's "better", but it's more legible to me.

// pedantic allows starting angle bracket without ending angle bracket
href = href.slice(1);
} else {
href = href.slice(1, -1);
}
}
calculuschild marked this conversation as resolved.
Show resolved Hide resolved
return outputLink(cap, {
href: href ? href.replace(this.rules.inline._escapes, '$1') : href,
title: title ? title.replace(this.rules.inline._escapes, '$1') : title
}, cap[0]);
return token;
}
}

Expand All @@ -502,8 +524,7 @@ module.exports = class Tokenizer {
text
};
}
const token = outputLink(cap, link, cap[0]);
return token;
return outputLink(cap, link, cap[0]);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ inline.tag = edit(inline.tag)
.getRegex();

inline._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
inline._href = /<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/;
inline._href = /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/;
inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;

inline.link = edit(inline.link)
Expand Down
12 changes: 4 additions & 8 deletions test/specs/commonmark/commonmark.0.29.json
Original file line number Diff line number Diff line change
Expand Up @@ -3939,8 +3939,7 @@
"example": 486,
"start_line": 7543,
"end_line": 7547,
"section": "Links",
"shouldFail": true
"section": "Links"
},
{
"markdown": "[link](foo\nbar)\n",
Expand All @@ -3964,26 +3963,23 @@
"example": 489,
"start_line": 7571,
"end_line": 7575,
"section": "Links",
"shouldFail": true
"section": "Links"
},
{
"markdown": "[link](<foo\\>)\n",
"html": "<p>[link](&lt;foo&gt;)</p>\n",
"example": 490,
"start_line": 7579,
"end_line": 7583,
"section": "Links",
"shouldFail": true
"section": "Links"
},
{
"markdown": "[a](<b)c\n[a](<b)c>\n[a](<b>c)\n",
"html": "<p>[a](&lt;b)c\n[a](&lt;b)c&gt;\n[a](<b>c)</p>\n",
"example": 491,
"start_line": 7588,
"end_line": 7596,
"section": "Links",
"shouldFail": true
"section": "Links"
},
{
"markdown": "[link](\\(foo\\))\n",
Expand Down
12 changes: 4 additions & 8 deletions test/specs/gfm/commonmark.0.29.json
Original file line number Diff line number Diff line change
Expand Up @@ -3939,8 +3939,7 @@
"example": 486,
"start_line": 7543,
"end_line": 7547,
"section": "Links",
"shouldFail": true
"section": "Links"
},
{
"markdown": "[link](foo\nbar)\n",
Expand All @@ -3964,26 +3963,23 @@
"example": 489,
"start_line": 7571,
"end_line": 7575,
"section": "Links",
"shouldFail": true
"section": "Links"
},
{
"markdown": "[link](<foo\\>)\n",
"html": "<p>[link](&lt;foo&gt;)</p>\n",
"example": 490,
"start_line": 7579,
"end_line": 7583,
"section": "Links",
"shouldFail": true
"section": "Links"
},
{
"markdown": "[a](<b)c\n[a](<b)c>\n[a](<b>c)\n",
"html": "<p>[a](&lt;b)c\n[a](&lt;b)c&gt;\n[a](<b>c)</p>\n",
"example": 491,
"start_line": 7588,
"end_line": 7596,
"section": "Links",
"shouldFail": true
"section": "Links"
},
{
"markdown": "[link](\\(foo\\))\n",
Expand Down
4 changes: 3 additions & 1 deletion test/specs/new/link_lt.html
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
<p><a href="%3Ctest">URL</a></p>
<p><a href="test">URL</a></p>

<p><a href="test%5C">URL</a></p>
5 changes: 5 additions & 0 deletions test/specs/new/link_lt.md
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
---
pedantic: true
---
[URL](<test)

[URL](<test\>)