Skip to content

Commit 73b52ba

Browse files
committed
Output ‘data-wikilink=“true”’ for wikilinks
This is so it’s possible to tell the difference between a normal link and a wikilink during post-processing.
1 parent 6828974 commit 73b52ba

File tree

7 files changed

+59
-11
lines changed

7 files changed

+59
-11
lines changed

src/cm.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,13 @@ impl<'a, 'o> CommonMarkFormatter<'a, 'o> {
367367
NodeValue::TaskItem(symbol) => self.format_task_item(symbol, node, entering),
368368
NodeValue::Strikethrough => self.format_strikethrough(),
369369
NodeValue::Superscript => self.format_superscript(),
370-
NodeValue::Link(ref nl) => return self.format_link(node, nl, entering),
370+
NodeValue::Link(ref nl) => {
371+
if nl.wikilink {
372+
return self.format_wikilink(nl, entering);
373+
} else {
374+
return self.format_link(node, nl, entering);
375+
}
376+
}
371377
NodeValue::Image(ref nl) => self.format_image(nl, allow_wrap, entering),
372378
#[cfg(feature = "shortcodes")]
373379
NodeValue::ShortCode(ref ne) => self.format_shortcode(ne, entering),
@@ -689,6 +695,24 @@ impl<'a, 'o> CommonMarkFormatter<'a, 'o> {
689695
true
690696
}
691697

698+
fn format_wikilink(&mut self, nl: &NodeLink, entering: bool) -> bool {
699+
if entering {
700+
write!(self, "[[").unwrap();
701+
if self.options.extension.wikilinks_title_after_pipe {
702+
self.output(nl.url.as_bytes(), false, Escaping::Url);
703+
write!(self, "|").unwrap();
704+
}
705+
} else {
706+
if self.options.extension.wikilinks_title_before_pipe {
707+
write!(self, "|").unwrap();
708+
self.output(nl.url.as_bytes(), false, Escaping::Url);
709+
}
710+
write!(self, "]]").unwrap();
711+
}
712+
713+
true
714+
}
715+
692716
fn format_image(&mut self, nl: &NodeLink, allow_wrap: bool, entering: bool) {
693717
if entering {
694718
write!(self, "![").unwrap();

src/html.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,9 @@ impl<'o> HtmlFormatter<'o> {
826826
self.output.write_all(b"\" title=\"")?;
827827
self.escape(nl.title.as_bytes())?;
828828
}
829+
if nl.wikilink {
830+
self.output.write_all(b"\" data-wikilink=\"true")?;
831+
}
829832
self.output.write_all(b"\">")?;
830833
} else {
831834
self.output.write_all(b"</a>")?;

src/nodes.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,9 @@ pub struct NodeLink {
251251
/// Note this field is used for the `title` attribute by the HTML formatter even for images;
252252
/// `alt` text is supplied in the image inline text.
253253
pub title: String,
254+
255+
/// Whether this is a wikilink or not
256+
pub wikilink: bool,
254257
}
255258

256259
/// The metadata of a list; the kind of list, the delimiter used and so on.

src/parser/autolink.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ fn www_match<'a>(
122122
NodeValue::Link(NodeLink {
123123
url,
124124
title: String::new(),
125+
wikilink: false,
125126
}),
126127
(0, 1, 0, 1).into(),
127128
);
@@ -290,6 +291,7 @@ fn url_match<'a>(
290291
NodeValue::Link(NodeLink {
291292
url: url.clone(),
292293
title: String::new(),
294+
wikilink: false,
293295
}),
294296
(0, 1, 0, 1).into(),
295297
);
@@ -398,6 +400,7 @@ fn email_match<'a>(
398400
NodeValue::Link(NodeLink {
399401
url,
400402
title: String::new(),
403+
wikilink: false,
401404
}),
402405
(0, 1, 0, 1).into(),
403406
);

src/parser/inlines.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1491,7 +1491,11 @@ impl<'a, 'r, 'o, 'd, 'i, 'c, 'subj> Subject<'a, 'r, 'o, 'd, 'i, 'c, 'subj> {
14911491
pub fn close_bracket_match(&mut self, is_image: bool, url: String, title: String) {
14921492
let brackets_len = self.brackets.len();
14931493

1494-
let nl = NodeLink { url, title };
1494+
let nl = NodeLink {
1495+
url,
1496+
title,
1497+
wikilink: false,
1498+
};
14951499
let inl = self.make_inline(
14961500
if is_image {
14971501
NodeValue::Image(nl)
@@ -1585,6 +1589,7 @@ impl<'a, 'r, 'o, 'd, 'i, 'c, 'subj> Subject<'a, 'r, 'o, 'd, 'i, 'c, 'subj> {
15851589
let nl = NodeLink {
15861590
url: String::from_utf8(url_clean).unwrap(),
15871591
title: String::new(),
1592+
wikilink: true,
15881593
};
15891594
let inl = self.make_inline(NodeValue::Link(nl), startpos - 1, self.pos - 1);
15901595
inl.append(self.make_inline(
@@ -1724,6 +1729,7 @@ impl<'a, 'r, 'o, 'd, 'i, 'c, 'subj> Subject<'a, 'r, 'o, 'd, 'i, 'c, 'subj> {
17241729
NodeValue::Link(NodeLink {
17251730
url: String::from_utf8(strings::clean_autolink(url, kind)).unwrap(),
17261731
title: String::new(),
1732+
wikilink: false,
17271733
}),
17281734
start_column + 1,
17291735
end_column + 1,

src/parser/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ pub struct ExtensionOptions {
435435
/// let mut options = Options::default();
436436
/// options.extension.wikilinks_title_after_pipe = true;
437437
/// assert_eq!(markdown_to_html("[[url|link text]]", &options),
438-
/// "<p><a href=\"url\">link text</a></p>\n");
438+
/// "<p><a href=\"url\" data-wikilink=\"true\">link text</a></p>\n");
439439
/// ```
440440
pub wikilinks_title_after_pipe: bool,
441441

@@ -450,7 +450,7 @@ pub struct ExtensionOptions {
450450
/// let mut options = Options::default();
451451
/// options.extension.wikilinks_title_before_pipe = true;
452452
/// assert_eq!(markdown_to_html("[[link text|url]]", &options),
453-
/// "<p><a href=\"url\">link text</a></p>\n");
453+
/// "<p><a href=\"url\" data-wikilink=\"true\">link text</a></p>\n");
454454
/// ```
455455
pub wikilinks_title_before_pipe: bool,
456456
}

src/tests/wikilinks.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
11
use super::*;
22

3+
#[test]
4+
fn wikilinks_escaped_chars_in_link_text() {
5+
html_opts!(
6+
[extension.wikilinks_title_before_pipe],
7+
concat!("[[a \\<link|url]]",),
8+
concat!("<p><a href=\"url\" data-wikilink=\"true\">a &lt;link</a></p>\n"),
9+
);
10+
}
11+
312
#[test]
413
fn wikilinks_does_not_unescape_html_entities_in_link_text() {
514
html_opts!(
6-
[extension.wikilinks_title_after_pipe],
15+
[extension.wikilinks_title_after_pipe, render.escaped_char_spans],
716
concat!("This is [[&lt;script&gt;alert(0)&lt;/script&gt;|a &lt;link]]",),
8-
concat!("<p>This is <a href=\"%3Cscript%3Ealert(0)%3C/script%3E\">a &lt;link</a></p>\n"),
17+
concat!("<p>This is <a href=\"%3Cscript%3Ealert(0)%3C/script%3E\" data-wikilink=\"true\">a &lt;link</a></p>\n"),
918
);
1019

1120
html_opts!(
1221
[extension.wikilinks_title_before_pipe],
1322
concat!("This is [[a &lt;link|&lt;script&gt;alert(0)&lt;/script&gt;]]",),
14-
concat!("<p>This is <a href=\"%3Cscript%3Ealert(0)%3C/script%3E\">a &lt;link</a></p>\n"),
23+
concat!("<p>This is <a href=\"%3Cscript%3Ealert(0)%3C/script%3E\" data-wikilink=\"true\">a &lt;link</a></p>\n"),
1524
);
1625
}
1726

@@ -20,13 +29,13 @@ fn wikilinks_sanitizes_the_href_attribute_case_1() {
2029
html_opts!(
2130
[extension.wikilinks_title_after_pipe],
2231
concat!("[[http:\'\"injected=attribute&gt;&lt;img/src=\"0\"onerror=\"alert(0)\"&gt;https://example.com|a]]",),
23-
concat!("<p><a href=\"http:&#x27;%22injected=attribute%3E%3Cimg/src=%220%22onerror=%22alert(0)%22%3Ehttps://example.com\">a</a></p>\n"),
32+
concat!("<p><a href=\"http:&#x27;%22injected=attribute%3E%3Cimg/src=%220%22onerror=%22alert(0)%22%3Ehttps://example.com\" data-wikilink=\"true\">a</a></p>\n"),
2433
);
2534

2635
html_opts!(
2736
[extension.wikilinks_title_before_pipe],
2837
concat!("[[a|http:\'\"injected=attribute&gt;&lt;img/src=\"0\"onerror=\"alert(0)\"&gt;https://example.com]]",),
29-
concat!("<p><a href=\"http:&#x27;%22injected=attribute%3E%3Cimg/src=%220%22onerror=%22alert(0)%22%3Ehttps://example.com\">a</a></p>\n"),
38+
concat!("<p><a href=\"http:&#x27;%22injected=attribute%3E%3Cimg/src=%220%22onerror=%22alert(0)%22%3Ehttps://example.com\" data-wikilink=\"true\">a</a></p>\n"),
3039
);
3140
}
3241

@@ -35,13 +44,13 @@ fn wikilinks_sanitizes_the_href_attribute_case_2() {
3544
html_opts!(
3645
[extension.wikilinks_title_after_pipe],
3746
concat!("<i>[[\'\"&gt;&lt;svg&gt;&lt;i/class=gl-show-field-errors&gt;&lt;input/title=\"&lt;script&gt;alert(0)&lt;/script&gt;\"/&gt;&lt;/svg&gt;https://example.com|a]]",),
38-
concat!("<p><!-- raw HTML omitted --><a href=\"&#x27;%22%3E%3Csvg%3E%3Ci/class=gl-show-field-errors%3E%3Cinput/title=%22%3Cscript%3Ealert(0)%3C/script%3E%22/%3E%3C/svg%3Ehttps://example.com\">a</a></p>\n"),
47+
concat!("<p><!-- raw HTML omitted --><a href=\"&#x27;%22%3E%3Csvg%3E%3Ci/class=gl-show-field-errors%3E%3Cinput/title=%22%3Cscript%3Ealert(0)%3C/script%3E%22/%3E%3C/svg%3Ehttps://example.com\" data-wikilink=\"true\">a</a></p>\n"),
3948
);
4049

4150
html_opts!(
4251
[extension.wikilinks_title_before_pipe],
4352
concat!("<i>[[a|\'\"&gt;&lt;svg&gt;&lt;i/class=gl-show-field-errors&gt;&lt;input/title=\"&lt;script&gt;alert(0)&lt;/script&gt;\"/&gt;&lt;/svg&gt;https://example.com]]",),
44-
concat!("<p><!-- raw HTML omitted --><a href=\"&#x27;%22%3E%3Csvg%3E%3Ci/class=gl-show-field-errors%3E%3Cinput/title=%22%3Cscript%3Ealert(0)%3C/script%3E%22/%3E%3C/svg%3Ehttps://example.com\">a</a></p>\n"),
53+
concat!("<p><!-- raw HTML omitted --><a href=\"&#x27;%22%3E%3Csvg%3E%3Ci/class=gl-show-field-errors%3E%3Cinput/title=%22%3Cscript%3Ealert(0)%3C/script%3E%22/%3E%3C/svg%3Ehttps://example.com\" data-wikilink=\"true\">a</a></p>\n"),
4554
);
4655
}
4756

0 commit comments

Comments
 (0)