OLD | NEW |
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /// The line contains only whitespace or is empty. | 5 /// The line contains only whitespace or is empty. |
6 final _RE_EMPTY = const RegExp(@'^([ \t]*)$'); | 6 final _RE_EMPTY = const RegExp(@'^([ \t]*)$'); |
7 | 7 |
8 /// A series of "=" or "-" (on the next line) define setext-style headers. | 8 /// A series of `=` or `-` (on the next line) define setext-style headers. |
9 final _RE_SETEXT = const RegExp(@'^((=+)|(-+))$'); | 9 final _RE_SETEXT = const RegExp(@'^((=+)|(-+))$'); |
10 | 10 |
11 /// Leading (and trailing) "#" define atx-style headers. | 11 /// Leading (and trailing) `#` define atx-style headers. |
12 final _RE_HEADER = const RegExp(@'^(#{1,6})(.*?)#*$'); | 12 final _RE_HEADER = const RegExp(@'^(#{1,6})(.*?)#*$'); |
13 | 13 |
14 /// The line starts with ">" with one optional space after. | 14 /// The line starts with `>` with one optional space after. |
15 final _RE_BLOCKQUOTE = const RegExp(@'^[ ]{0,3}>[ ]?(.*)$'); | 15 final _RE_BLOCKQUOTE = const RegExp(@'^[ ]{0,3}>[ ]?(.*)$'); |
16 | 16 |
17 /// A line indented four spaces. Used for code blocks and lists. | 17 /// A line indented four spaces. Used for code blocks and lists. |
18 final _RE_INDENT = const RegExp(@'^(?: |\t)(.*)$'); | 18 final _RE_INDENT = const RegExp(@'^(?: |\t)(.*)$'); |
19 | 19 |
20 /// Three or more hyphens, asterisks or underscores by themselves. Note that | 20 /// Three or more hyphens, asterisks or underscores by themselves. Note that |
21 /// a line like "----" is valid as both HR and SETEXT. In case of a tie, | 21 /// a line like `----` is valid as both HR and SETEXT. In case of a tie, |
22 /// SETEXT should win. | 22 /// SETEXT should win. |
23 final _RE_HR = const RegExp(@'^[ ]{0,3}((-+[ ]{0,2}){3,}|' + | 23 final _RE_HR = const RegExp(@'^[ ]{0,3}((-+[ ]{0,2}){3,}|' + |
24 @'(_+[ ]{0,2}){3,}|' + | 24 @'(_+[ ]{0,2}){3,}|' + |
25 @'(\*+[ ]{0,2}){3,})$'); | 25 @'(\*+[ ]{0,2}){3,})$'); |
26 | 26 |
27 /// Really hacky way to detect block-level embedded HTML. Just looks for | 27 /// Really hacky way to detect block-level embedded HTML. Just looks for |
28 /// "<somename". | 28 /// "<somename". |
29 final _RE_HTML = const RegExp(@'^<[ ]*\w+[ >]'); | 29 final _RE_HTML = const RegExp(@'^<[ ]*\w+[ >]'); |
30 | 30 |
31 /// A line starting with one of these markers: "-", "*", "+". May have up to | 31 /// A line starting with one of these markers: `-`, `*`, `+`. May have up to |
32 /// three leading spaces before the marker and any number of spaces or tabs | 32 /// three leading spaces before the marker and any number of spaces or tabs |
33 /// after. | 33 /// after. |
34 final _RE_UL = const RegExp(@'^[ ]{0,3}[*+-][ \t]+(.*)$'); | 34 final _RE_UL = const RegExp(@'^[ ]{0,3}[*+-][ \t]+(.*)$'); |
35 | 35 |
36 /// A line starting with a number like "123.". May have up to three leading | 36 /// A line starting with a number like `123.`. May have up to three leading |
37 /// spaces before the marker and any number of spaces or tabs after. | 37 /// spaces before the marker and any number of spaces or tabs after. |
38 final _RE_OL = const RegExp(@'^[ ]{0,3}\d+\.[ \t]+(.*)$'); | 38 final _RE_OL = const RegExp(@'^[ ]{0,3}\d+\.[ \t]+(.*)$'); |
39 | 39 |
40 /// Maintains the internal state needed to parse a series of lines into blocks | 40 /// Maintains the internal state needed to parse a series of lines into blocks |
41 /// of markdown suitable for further inline parsing. | 41 /// of markdown suitable for further inline parsing. |
42 class BlockParser { | 42 class BlockParser { |
43 final List<String> lines; | 43 final List<String> lines; |
44 | 44 |
45 /// The markdown document this parser is parsing. | 45 /// The markdown document this parser is parsing. |
46 final Document document; | 46 final Document document; |
47 | 47 |
48 /// Index of the current line. | 48 /// Index of the current line. |
49 int pos; | 49 int pos; |
50 | 50 |
51 BlockParser(this.lines, this.document) | 51 BlockParser(this.lines, this.document) |
52 : pos = 0; | 52 : pos = 0; |
53 | 53 |
54 /// Gets the current line. | 54 /// Gets the current line. |
55 String get current() => lines[pos]; | 55 String get current() => lines[pos]; |
56 | 56 |
57 /// Gets the line after the current one or `null` if there is none. | 57 /// Gets the line after the current one or `null` if there is none. |
58 String get next() { | 58 String get next() { |
59 // Don't read past the end. | 59 // Don't read past the end. |
60 if (pos >= lines.length - 1) return null; | 60 if (pos >= lines.length - 1) return null; |
61 return lines[pos + 1]; | 61 return lines[pos + 1]; |
62 } | 62 } |
63 | 63 |
64 void advance() => pos++; | 64 void advance() { |
| 65 pos++; |
| 66 } |
| 67 |
65 bool get isDone() => pos >= lines.length; | 68 bool get isDone() => pos >= lines.length; |
66 | 69 |
67 /// Gets whether or not the current line matches the given pattern. | 70 /// Gets whether or not the current line matches the given pattern. |
68 bool matches(RegExp regex) { | 71 bool matches(RegExp regex) { |
69 if (isDone) return false; | 72 if (isDone) return false; |
70 return regex.firstMatch(current) != null; | 73 return regex.firstMatch(current) != null; |
71 } | 74 } |
72 | 75 |
73 /// Gets whether or not the current line matches the given pattern. | 76 /// Gets whether or not the current line matches the given pattern. |
74 bool matchesNext(RegExp regex) { | 77 bool matchesNext(RegExp regex) { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
106 RegExp get pattern() => null; | 109 RegExp get pattern() => null; |
107 | 110 |
108 bool get canEndBlock() => true; | 111 bool get canEndBlock() => true; |
109 | 112 |
110 bool canParse(BlockParser parser) { | 113 bool canParse(BlockParser parser) { |
111 return pattern.firstMatch(parser.current) != null; | 114 return pattern.firstMatch(parser.current) != null; |
112 } | 115 } |
113 | 116 |
114 abstract Node parse(BlockParser parser); | 117 abstract Node parse(BlockParser parser); |
115 | 118 |
116 List<Node> parseChildLines(BlockParser parser) { | 119 List<String> parseChildLines(BlockParser parser) { |
117 // Grab all of the lines that form the blockquote, stripping off the ">". | 120 // Grab all of the lines that form the blockquote, stripping off the ">". |
118 final childLines = []; | 121 final childLines = <String>[]; |
119 | 122 |
120 while (!parser.isDone) { | 123 while (!parser.isDone) { |
121 final match = pattern.firstMatch(parser.current); | 124 final match = pattern.firstMatch(parser.current); |
122 if (match == null) break; | 125 if (match == null) break; |
123 childLines.add(match.group(1)); | 126 childLines.add(match.group(1)); |
124 parser.advance(); | 127 parser.advance(); |
125 } | 128 } |
126 | 129 |
127 return childLines; | 130 return childLines; |
128 } | 131 } |
(...skipping 29 matching lines...) Expand all Loading... |
158 | 161 |
159 final tag = (match.group(1)[0] == '=') ? 'h1' : 'h2'; | 162 final tag = (match.group(1)[0] == '=') ? 'h1' : 'h2'; |
160 final contents = parser.document.parseInline(parser.current); | 163 final contents = parser.document.parseInline(parser.current); |
161 parser.advance(); | 164 parser.advance(); |
162 parser.advance(); | 165 parser.advance(); |
163 | 166 |
164 return new Element(tag, contents); | 167 return new Element(tag, contents); |
165 } | 168 } |
166 } | 169 } |
167 | 170 |
168 /// Parses atx-style headers: "## Header ##". | 171 /// Parses atx-style headers: `## Header ##`. |
169 class HeaderSyntax extends BlockSyntax { | 172 class HeaderSyntax extends BlockSyntax { |
170 RegExp get pattern() => _RE_HEADER; | 173 RegExp get pattern() => _RE_HEADER; |
171 | 174 |
172 Node parse(BlockParser parser) { | 175 Node parse(BlockParser parser) { |
173 final match = pattern.firstMatch(parser.current); | 176 final match = pattern.firstMatch(parser.current); |
174 parser.advance(); | 177 parser.advance(); |
175 final level = match.group(1).length; | 178 final level = match.group(1).length; |
176 final contents = parser.document.parseInline(match.group(2).trim()); | 179 final contents = parser.document.parseInline(match.group(2).trim()); |
177 return new Element('h$level', contents); | 180 return new Element('h$level', contents); |
178 } | 181 } |
179 } | 182 } |
180 | 183 |
181 /// Parses email-style blockquotes: "> quote". | 184 /// Parses email-style blockquotes: `> quote`. |
182 class BlockquoteSyntax extends BlockSyntax { | 185 class BlockquoteSyntax extends BlockSyntax { |
183 RegExp get pattern() => _RE_BLOCKQUOTE; | 186 RegExp get pattern() => _RE_BLOCKQUOTE; |
184 | 187 |
185 Node parse(BlockParser parser) { | 188 Node parse(BlockParser parser) { |
186 final childLines = parseChildLines(parser); | 189 final childLines = parseChildLines(parser); |
187 | 190 |
188 // Recursively parse the contents of the blockquote. | 191 // Recursively parse the contents of the blockquote. |
189 final children = parser.document.parseLines(childLines); | 192 final children = parser.document.parseLines(childLines); |
190 | 193 |
191 return new Element('blockquote', children); | 194 return new Element('blockquote', children); |
(...skipping 10 matching lines...) Expand all Loading... |
202 // The Markdown tests expect a trailing newline. | 205 // The Markdown tests expect a trailing newline. |
203 childLines.add(''); | 206 childLines.add(''); |
204 | 207 |
205 // Escape the code. | 208 // Escape the code. |
206 final escaped = escapeHtml(Strings.join(childLines, '\n')); | 209 final escaped = escapeHtml(Strings.join(childLines, '\n')); |
207 | 210 |
208 return new Element('pre', [new Element.text('code', escaped)]); | 211 return new Element('pre', [new Element.text('code', escaped)]); |
209 } | 212 } |
210 } | 213 } |
211 | 214 |
212 /// Parses horizontal rules like "---", "_ _ _", "* * *", etc. | 215 /// Parses horizontal rules like `---`, `_ _ _`, `* * *`, etc. |
213 class HorizontalRuleSyntax extends BlockSyntax { | 216 class HorizontalRuleSyntax extends BlockSyntax { |
214 RegExp get pattern() => _RE_HR; | 217 RegExp get pattern() => _RE_HR; |
215 | 218 |
216 Node parse(BlockParser parser) { | 219 Node parse(BlockParser parser) { |
217 final match = pattern.firstMatch(parser.current); | 220 final match = pattern.firstMatch(parser.current); |
218 parser.advance(); | 221 parser.advance(); |
219 return new Element.empty('hr'); | 222 return new Element.empty('hr'); |
220 } | 223 } |
221 } | 224 } |
222 | 225 |
223 /// Parses inline HTML at the block level. This differs from other markdown | 226 /// Parses inline HTML at the block level. This differs from other markdown |
224 /// implementations in several ways: | 227 /// implementations in several ways: |
225 /// | 228 /// |
226 /// 1. This one is way way WAY simpler. | 229 /// 1. This one is way way WAY simpler. |
227 /// 2. All HTML tags at the block level will be treated as blocks. If you start | 230 /// 2. All HTML tags at the block level will be treated as blocks. If you |
228 /// a paragraph with <em>, it will not wrap it in a <p> for you. As soon as | 231 /// start a paragraph with `<em>`, it will not wrap it in a `<p>` for you. |
229 /// it sees something like HTML, it stops mucking with it until it hits the | 232 /// As soon as it sees something like HTML, it stops mucking with it until |
230 /// next block. | 233 /// it hits the next block. |
231 /// 3. Absolutely no HTML parsing or validation is done. We're a markdown | 234 /// 3. Absolutely no HTML parsing or validation is done. We're a markdown |
232 /// parser not an HTML parser! | 235 /// parser not an HTML parser! |
233 class BlockHtmlSyntax extends BlockSyntax { | 236 class BlockHtmlSyntax extends BlockSyntax { |
234 RegExp get pattern() => _RE_HTML; | 237 RegExp get pattern() => _RE_HTML; |
235 | 238 |
236 bool get canEndBlock() => false; | 239 bool get canEndBlock() => false; |
237 | 240 |
238 Node parse(BlockParser parser) { | 241 Node parse(BlockParser parser) { |
239 final childLines = []; | 242 final childLines = []; |
240 | 243 |
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
424 while (!isAtBlockEnd(parser)) { | 427 while (!isAtBlockEnd(parser)) { |
425 childLines.add(parser.current); | 428 childLines.add(parser.current); |
426 parser.advance(); | 429 parser.advance(); |
427 } | 430 } |
428 | 431 |
429 final contents = parser.document.parseInline( | 432 final contents = parser.document.parseInline( |
430 Strings.join(childLines, '\n')); | 433 Strings.join(childLines, '\n')); |
431 return new Element('p', contents); | 434 return new Element('p', contents); |
432 } | 435 } |
433 } | 436 } |
OLD | NEW |