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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
use std::str;
use parser::{MarkdownParser, PhantomMark, Success, End, NoParse};
use tokens::*;
use util::CharOps;
use super::InlineParser;
pub trait EmphasisParser {
fn parse_emphasis(&self, ec: u8, n: uint) -> Option<Inline>;
}
impl<'a> EmphasisParser for MarkdownParser<'a> {
fn parse_emphasis(&self, ec: u8, n: uint) -> Option<Inline> {
debug!("reading emphasis, char [{}], n = {}", ec.to_ascii(), n);
let pm = self.cur.phantom_mark();
loop {
// a marker over the first character of closing emphasis
let pm_last = opt_ret!(self.until_emph_closing(ec, n));
let slice = self.cur.slice(pm, pm_last);
debug!("checking slice: [{}], n: {}", ::std::str::from_utf8(slice).unwrap(), n);
// escaped closing emphasis
if slice[slice.len()-1] != b' ' {
if ec.is_code() { // this is code inline
return Some(Code(str::from_utf8(slice).unwrap().into_string()));
} else {
let subp = self.fork(slice);
let result = self.fix_links(subp.parse_inline());
return Some(match n {
1 => Emphasis(result),
2 => MoreEmphasis(result),
_ => unreachable!() // for now
});
}
}
}
}
}
trait Ops<'a> {
fn until_emph_closing(&self, ec: u8, n: uint) -> Option<PhantomMark>;
}
impl<'a> Ops<'a> for MarkdownParser<'a> {
fn until_emph_closing(&self, ec: u8, n: uint) -> Option<PhantomMark> {
assert!(n > 0); // need at least one emphasis character
let pm = self.cur.phantom_mark();
let mut pm_last = pm;
let mut escaping = false;
macro_rules! advance(
() => (pm_last = self.cur.phantom_mark())
)
macro_rules! retract(
() => (pm_last = pm)
)
'outer: loop {
let c = match self.cur.next_byte() {
Some(c) => c,
None => return None // if we're here then we haven't found the closing "brace"
};
match c {
// pass escaped characters as is
b'\\' => escaping = true,
_ if escaping => escaping = false,
// found our emphasis character
c if c == ec => {
// TODO: make something more pretty
self.cur.prev();
advance!();
self.cur.next();
let m = self.cur.mark();
let mut rn = 1;
loop {
match self.try_read_char(ec) {
Success(_) => rn += 1,
_ => break
}
}
if rn == n { // this is our emphasis boundary, finish reading
m.cancel();
break;
} else {
// otherwise reset to the character just after the one
// we have just read
retract!();
m.reset();
}
}
// inline code block started, and we're not an inline code block ourselves
// we need to pass through this block as is
c if c == b'`' => {
// count `s
let mut sn = 1u;
while break_on_end!(self.try_read_char(b'`')).is_success() {
sn += 1;
}
let mut ec_mark = None;
// read until closing delimiter
let mut tn = 0;
while tn < sn {
match self.cur.next_byte() {
Some(b'`') => tn += 1,
Some(cc) => {
tn = 0;
if cc == ec {
advance!();
ec_mark = Some(self.cur.mark());
}
}
None => break 'outer
}
}
retract!();
ec_mark.map(|m| m.cancel());
}
// skip hyperlinks
c if c == b'[' => {
debug!("encountered link start");
let mut ec_mark = None;
// read until closing brace
loop {
match self.cur.next_byte() {
Some(b']') => break,
Some(c) if ec_mark.is_none() && c == ec && self.lookahead_chars(n-1, ec) => {
debug!("encountered emphasis inside link, setting a mark");
advance!();
ec_mark = Some(self.cur.mark());
}
Some(_) => {}
None => break 'outer
}
}
debug!("read first link part, skipping whitespace");
// skip whitespace between delimiting braces
parse_or_break!(self.skip_spaces_and_newlines());
debug!("skipped whitespace, current char: {}", self.cur.to_ascii());
// determine closing brace for the second part of the link
let cc = match *self.cur {
b'[' => b']',
b'(' => b')',
_ => if ec_mark.is_some() { break 'outer } else { continue 'outer }
};
self.cur.next();
debug!("expected closing character is {}, skipping rest of the link", cc.to_ascii());
// skip second part of the link
loop {
match self.cur.next_byte() {
Some(c) if c == cc => break,
Some(c) if ec_mark.is_none() && c == ec && self.lookahead_chars(n-1, ec) => {
debug!("encountered emphasis inside link second part, setting a mark");
advance!();
ec_mark = Some(self.cur.mark());
}
Some(_) => {}
None => break 'outer
}
}
debug!("everything skipped, resetting marks");
retract!();
ec_mark.map(|m| m.cancel());
}
// just pass through any other character
_ => {}
}
}
if pm_last == pm { None } else { Some(pm_last) }
}
}