1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
use std::str;
use parser::{MarkdownParser, Success, End, NoParse};
use tokens::*;
use util::{ByteSliceOps, CharOps};
pub trait LinkParser {
fn parse_link(&self, is_image: bool) -> Option<Inline>;
}
impl<'a> LinkParser for MarkdownParser<'a> {
fn parse_link(&self, is_image: bool) -> Option<Inline> {
let pm = self.cur.phantom_mark();
let label;
// find matching closing brace
let mut escaping = false;
let mut level = 1u;
loop {
let c = opt_ret!(self.cur.next_byte());
match c {
b'\\' => escaping = true,
_ if escaping => escaping = false,
b'[' => level += 1,
b']' => {
level -= 1;
if level <= 0 { break; }
}
_ => {}
}
}
label = self.cur.slice_until_now_from(pm);
// if this is shortcut link, we'll return here
let m = self.cur.mark();
// TODO: footnote links?
// skip spaces
self.skip_spaces_and_newlines();
let mut link = None;
let mut title = None;
let mut id = None;
match self.cur.current_byte() {
Some(b'(') => { // inline link
self.cur.next();
// skip initial whitespace
parse_or_ret_none!(self.skip_spaces_and_newlines());
let pm = self.cur.phantom_mark();
// read until link end, balancing parentheses
let mut level = 0u;
loop {
let c = opt_ret!(self.cur.next_byte());
match c {
b'\\' => { self.cur.next(); }, // skip escaped char
b'(' => level += 1,
b')' => if level == 0 { break; } else { level -= 1; },
// encountered link title
cc if (cc == b'\'' || cc == b'"') &&
self.cur.peek_before_prev().is_space() => break,
_ => {} // just pass through
}
}
let link_slice = self.cur.slice_until_now_from(pm);
debug!("read link slice: {}", str::from_utf8(link_slice).unwrap());
// read title, if it is there
let pc = self.cur.peek_prev();
if pc == b'\'' || pc == b'\"' { // title
let pm = self.cur.phantom_mark();
let mut read_title = false;
loop {
let c = opt_ret!(self.cur.next_byte());
match c {
b'\\' => { self.cur.next(); }, // skip escaped byte
cc if cc == pc && !read_title => {
title = Some(self.cur.slice_until_now_from(pm));
read_title = true;
}
b')' if read_title => break,
_ => {}
}
}
}
link = Some(
link_slice.trim_right(|b: u8| b.is_space())
.trim_left_one(b'<').trim_right_one(b'>')
);
m.cancel();
}
Some(b'[') => { // reference link
self.cur.next();
let pm = self.cur.phantom_mark();
loop {
let c = opt_ret!(self.cur.next_byte());
match c {
b']' => break,
_ => {}
}
}
id = Some(self.cur.slice_until_now_from(pm));
m.cancel();
}
_ => { // shortcut reference link
m.reset(); // revert to the first character after ']'
id = Some(label);
}
}
// TODO: parse link contents
let text = vec![Chunk(str::from_utf8(label).unwrap().into_string())];
let link = link.map(|link| str::from_utf8(link).unwrap().to_string());
let id = id.map(|id| str::from_utf8(id).unwrap().into_string());
let title = title.map(|title| str::from_utf8(title).unwrap().into_string());
let link = if is_image {
Image {
id: id,
link: link,
title: title,
alt: text
}
} else {
Link {
id: id,
link: link,
title: title,
text: Some(text)
}
};
Some(link)
}
}