Skip to content

Commit 1a84232

Browse files
authored
Merge pull request #386 from kivikakk/bw-fix-autolink-brackets
Return brackets in autolinks behavior back to cmark-gfm
2 parents 00ffbfe + a842fe9 commit 1a84232

File tree

2 files changed

+92
-10
lines changed

2 files changed

+92
-10
lines changed

Diff for: src/parser/autolink.rs

+25-10
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,13 @@ pub(crate) fn process_autolinks<'a>(
4747
}
4848
}
4949
b'w' => {
50-
post_org = www_match(arena, contents, i);
50+
post_org = www_match(arena, contents, i, relaxed_autolinks);
5151
if post_org.is_some() {
5252
break;
5353
}
5454
}
5555
b'@' => {
56-
post_org = email_match(arena, contents, i);
56+
post_org = email_match(arena, contents, i, relaxed_autolinks);
5757
if post_org.is_some() {
5858
break;
5959
}
@@ -85,6 +85,7 @@ fn www_match<'a>(
8585
arena: &'a Arena<AstNode<'a>>,
8686
contents: &[u8],
8787
i: usize,
88+
relaxed_autolinks: bool,
8889
) -> Option<(&'a AstNode<'a>, usize, usize)> {
8990
static WWW_DELIMS: Lazy<[bool; 256]> = Lazy::new(|| {
9091
let mut sc = [false; 256];
@@ -111,7 +112,7 @@ fn www_match<'a>(
111112
link_end += 1;
112113
}
113114

114-
link_end = autolink_delim(&contents[i..], link_end);
115+
link_end = autolink_delim(&contents[i..], link_end, relaxed_autolinks);
115116

116117
let mut url = "http://".to_string();
117118
url.push_str(str::from_utf8(&contents[i..link_end + i]).unwrap());
@@ -170,12 +171,10 @@ fn is_valid_hostchar(ch: char) -> bool {
170171
!ch.is_whitespace() && !ch.is_punctuation()
171172
}
172173

173-
fn autolink_delim(data: &[u8], mut link_end: usize) -> usize {
174+
fn autolink_delim(data: &[u8], mut link_end: usize, relaxed_autolinks: bool) -> usize {
174175
static LINK_END_ASSORTMENT: Lazy<[bool; 256]> = Lazy::new(|| {
175176
let mut sc = [false; 256];
176-
for c in &[
177-
b'?', b'!', b'.', b',', b':', b'*', b'_', b'~', b'\'', b'"', b'[', b']',
178-
] {
177+
for c in &[b'?', b'!', b'.', b',', b':', b'*', b'_', b'~', b'\'', b'"'] {
179178
sc[*c as usize] = true;
180179
}
181180
sc
@@ -191,7 +190,22 @@ fn autolink_delim(data: &[u8], mut link_end: usize) -> usize {
191190
while link_end > 0 {
192191
let cclose = data[link_end - 1];
193192

194-
let copen = if cclose == b')' { Some(b'(') } else { None };
193+
// Allow any number of matching parentheses (as recognised in copen/cclose)
194+
// at the end of the URL. If there is a greater number of closing
195+
// parentheses than opening ones, we remove one character from the end of
196+
// the link.
197+
let mut copen = if cclose == b')' { Some(b'(') } else { None };
198+
199+
if relaxed_autolinks && copen.is_none() {
200+
// allow balancing of `[]` and `{}` just like `()`
201+
copen = if cclose == b']' {
202+
Some(b'[')
203+
} else if cclose == b'}' {
204+
Some(b'{')
205+
} else {
206+
None
207+
};
208+
}
195209

196210
if LINK_END_ASSORTMENT[cclose as usize] {
197211
link_end -= 1;
@@ -266,7 +280,7 @@ fn url_match<'a>(
266280
link_end += 1;
267281
}
268282

269-
link_end = autolink_delim(&contents[i..], link_end);
283+
link_end = autolink_delim(&contents[i..], link_end, relaxed_autolinks);
270284

271285
let url = str::from_utf8(&contents[i - rewind..i + link_end])
272286
.unwrap()
@@ -292,6 +306,7 @@ fn email_match<'a>(
292306
arena: &'a Arena<AstNode<'a>>,
293307
contents: &[u8],
294308
i: usize,
309+
relaxed_autolinks: bool,
295310
) -> Option<(&'a AstNode<'a>, usize, usize)> {
296311
static EMAIL_OK_SET: Lazy<[bool; 256]> = Lazy::new(|| {
297312
let mut sc = [false; 256];
@@ -365,7 +380,7 @@ fn email_match<'a>(
365380
return None;
366381
}
367382

368-
link_end = autolink_delim(&contents[i..], link_end);
383+
link_end = autolink_delim(&contents[i..], link_end, relaxed_autolinks);
369384
if link_end == 0 {
370385
return None;
371386
}

Diff for: src/tests/autolink.rs

+67
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,51 @@ fn autolink_no_link_bad() {
5252
);
5353
}
5454

55+
#[test]
56+
fn autolink_parentheses_balanced() {
57+
let examples = [
58+
[
59+
"http://www.pokemon.com/Pikachu_(Electric)",
60+
"<p><a href=\"http://www.pokemon.com/Pikachu_(Electric)\">http://www.pokemon.com/Pikachu_(Electric)</a></p>\n",
61+
],
62+
[
63+
"http://www.pokemon.com/Pikachu_((Electric)",
64+
"<p><a href=\"http://www.pokemon.com/Pikachu_((Electric)\">http://www.pokemon.com/Pikachu_((Electric)</a></p>\n",
65+
],
66+
[
67+
"http://www.pokemon.com/Pikachu_(Electric))",
68+
"<p><a href=\"http://www.pokemon.com/Pikachu_(Electric)\">http://www.pokemon.com/Pikachu_(Electric)</a>)</p>\n",
69+
],
70+
[
71+
"http://www.pokemon.com/Pikachu_((Electric))",
72+
"<p><a href=\"http://www.pokemon.com/Pikachu_((Electric))\">http://www.pokemon.com/Pikachu_((Electric))</a></p>\n",
73+
],
74+
];
75+
76+
for example in examples {
77+
html_opts!([extension.autolink], example[0], example[1]);
78+
}
79+
80+
for example in examples {
81+
html_opts!(
82+
[extension.autolink, parse.relaxed_autolinks],
83+
example[0],
84+
example[1]
85+
);
86+
}
87+
}
88+
89+
#[test]
90+
fn autolink_brackets_unbalanced() {
91+
html_opts!(
92+
[extension.autolink],
93+
concat!("http://example.com/[abc]]...\n"),
94+
concat!(
95+
"<p><a href=\"http://example.com/%5Babc%5D%5D\">http://example.com/[abc]]</a>...</p>\n"
96+
),
97+
);
98+
}
99+
55100
#[test]
56101
fn autolink_ignore_links_in_brackets() {
57102
let examples = [
@@ -102,6 +147,28 @@ fn autolink_relaxed_links_in_brackets() {
102147
}
103148
}
104149

150+
#[test]
151+
fn autolink_relaxed_links_brackets_balanced() {
152+
html_opts!(
153+
[extension.autolink, parse.relaxed_autolinks],
154+
concat!("http://example.com/[abc]]...\n"),
155+
concat!(
156+
"<p><a href=\"http://example.com/%5Babc%5D\">http://example.com/[abc]</a>]...</p>\n"
157+
),
158+
);
159+
}
160+
161+
#[test]
162+
fn autolink_relaxed_links_curly_braces_balanced() {
163+
html_opts!(
164+
[extension.autolink, parse.relaxed_autolinks],
165+
concat!("http://example.com/{abc}}...\n"),
166+
concat!(
167+
"<p><a href=\"http://example.com/%7Babc%7D\">http://example.com/{abc}</a>}...</p>\n"
168+
),
169+
);
170+
}
171+
105172
#[test]
106173
fn autolink_relaxed_links_schemes() {
107174
let examples = [

0 commit comments

Comments
 (0)