Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(169)

Side by Side Diff: lib/src/block_parser.dart

Issue 1274763005: Clean up: (Closed) Base URL: https://github.com/dart-lang/markdown.git@master
Patch Set: Bump. Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/src/ast.dart ('k') | lib/src/document.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 library markdown.block_parser; 5 library markdown.block_parser;
6 6
7 import 'ast.dart'; 7 import 'ast.dart';
8 import 'document.dart'; 8 import 'document.dart';
9 import 'util.dart'; 9 import 'util.dart';
10 10
11 /// The line contains only whitespace or is empty. 11 /// The line contains only whitespace or is empty.
12 final _RE_EMPTY = new RegExp(r'^([ \t]*)$'); 12 final _emptyPattern = new RegExp(r'^([ \t]*)$');
13 13
14 /// A series of `=` or `-` (on the next line) define setext-style headers. 14 /// A series of `=` or `-` (on the next line) define setext-style headers.
15 final _RE_SETEXT = new RegExp(r'^((=+)|(-+))$'); 15 final _setextPattern = new RegExp(r'^((=+)|(-+))$');
16 16
17 /// Leading (and trailing) `#` define atx-style headers. 17 /// Leading (and trailing) `#` define atx-style headers.
18 final _RE_HEADER = new RegExp(r'^(#{1,6})(.*?)#*$'); 18 final _headerPattern = new RegExp(r'^(#{1,6})(.*?)#*$');
19 19
20 /// The line starts with `>` with one optional space after. 20 /// The line starts with `>` with one optional space after.
21 final _RE_BLOCKQUOTE = new RegExp(r'^[ ]{0,3}>[ ]?(.*)$'); 21 final _blockquotePattern = new RegExp(r'^[ ]{0,3}>[ ]?(.*)$');
22 22
23 /// A line indented four spaces. Used for code blocks and lists. 23 /// A line indented four spaces. Used for code blocks and lists.
24 final _RE_INDENT = new RegExp(r'^(?: |\t)(.*)$'); 24 final _indentPattern = new RegExp(r'^(?: |\t)(.*)$');
25 25
26 /// Fenced code block. 26 /// Fenced code block.
27 final _RE_CODE = new RegExp(r'^(`{3,}|~{3,})(.*)$'); 27 final _codePattern = new RegExp(r'^(`{3,}|~{3,})(.*)$');
28 28
29 /// Three or more hyphens, asterisks or underscores by themselves. Note that 29 /// Three or more hyphens, asterisks or underscores by themselves. Note that
30 /// a line like `----` is valid as both HR and SETEXT. In case of a tie, 30 /// a line like `----` is valid as both HR and SETEXT. In case of a tie,
31 /// SETEXT should win. 31 /// SETEXT should win.
32 final _RE_HR = new RegExp(r'^[ ]{0,3}((-+[ ]{0,2}){3,}|' 32 final _hrPattern = new RegExp(r'^[ ]{0,3}((-+[ ]{0,2}){3,}|'
33 r'(_+[ ]{0,2}){3,}|' 33 r'(_+[ ]{0,2}){3,}|'
34 r'(\*+[ ]{0,2}){3,})$'); 34 r'(\*+[ ]{0,2}){3,})$');
35 35
36 /// Really hacky way to detect block-level embedded HTML. Just looks for 36 /// Really hacky way to detect block-level embedded HTML. Just looks for
37 /// "<somename". 37 /// "<somename".
38 final _RE_HTML = new RegExp(r'^<[ ]*\w+[ >]'); 38 final _htmlPattern = new RegExp(r'^<[ ]*\w+[ >]');
39 39
40 /// A line starting with one of these markers: `-`, `*`, `+`. May have up to 40 /// A line starting with one of these markers: `-`, `*`, `+`. May have up to
41 /// three leading spaces before the marker and any number of spaces or tabs 41 /// three leading spaces before the marker and any number of spaces or tabs
42 /// after. 42 /// after.
43 final _RE_UL = new RegExp(r'^[ ]{0,3}[*+-][ \t]+(.*)$'); 43 final _ulPattern = new RegExp(r'^[ ]{0,3}[*+-][ \t]+(.*)$');
44 44
45 /// A line starting with a number like `123.`. May have up to three leading 45 /// A line starting with a number like `123.`. May have up to three leading
46 /// spaces before the marker and any number of spaces or tabs after. 46 /// spaces before the marker and any number of spaces or tabs after.
47 final _RE_OL = new RegExp(r'^[ ]{0,3}\d+\.[ \t]+(.*)$'); 47 final _olPattern = new RegExp(r'^[ ]{0,3}\d+\.[ \t]+(.*)$');
48 48
49 /// Maintains the internal state needed to parse a series of lines into blocks 49 /// Maintains the internal state needed to parse a series of lines into blocks
50 /// of markdown suitable for further inline parsing. 50 /// of markdown suitable for further inline parsing.
51 class BlockParser { 51 class BlockParser {
52 final List<String> lines; 52 final List<String> lines;
53 53
54 /// The markdown document this parser is parsing. 54 /// The markdown document this parser is parsing.
55 final Document document; 55 final Document document;
56 56
57 /// Index of the current line. 57 /// Index of the current line.
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
113 bool get canEndBlock => true; 113 bool get canEndBlock => true;
114 114
115 bool canParse(BlockParser parser) { 115 bool canParse(BlockParser parser) {
116 return pattern.firstMatch(parser.current) != null; 116 return pattern.firstMatch(parser.current) != null;
117 } 117 }
118 118
119 Node parse(BlockParser parser); 119 Node parse(BlockParser parser);
120 120
121 List<String> parseChildLines(BlockParser parser) { 121 List<String> parseChildLines(BlockParser parser) {
122 // Grab all of the lines that form the blockquote, stripping off the ">". 122 // Grab all of the lines that form the blockquote, stripping off the ">".
123 final childLines = <String>[]; 123 var childLines = <String>[];
124 124
125 while (!parser.isDone) { 125 while (!parser.isDone) {
126 final match = pattern.firstMatch(parser.current); 126 var match = pattern.firstMatch(parser.current);
127 if (match == null) break; 127 if (match == null) break;
128 childLines.add(match[1]); 128 childLines.add(match[1]);
129 parser.advance(); 129 parser.advance();
130 } 130 }
131 131
132 return childLines; 132 return childLines;
133 } 133 }
134 134
135 /// Gets whether or not [parser]'s current line should end the previous block. 135 /// Gets whether or not [parser]'s current line should end the previous block.
136 static bool isAtBlockEnd(BlockParser parser) { 136 static bool isAtBlockEnd(BlockParser parser) {
137 if (parser.isDone) return true; 137 if (parser.isDone) return true;
138 return syntaxes.any((s) => s.canParse(parser) && s.canEndBlock); 138 return syntaxes.any((s) => s.canParse(parser) && s.canEndBlock);
139 } 139 }
140 } 140 }
141 141
142 class EmptyBlockSyntax extends BlockSyntax { 142 class EmptyBlockSyntax extends BlockSyntax {
143 RegExp get pattern => _RE_EMPTY; 143 RegExp get pattern => _emptyPattern;
144 144
145 const EmptyBlockSyntax(); 145 const EmptyBlockSyntax();
146 146
147 Node parse(BlockParser parser) { 147 Node parse(BlockParser parser) {
148 parser.advance(); 148 parser.advance();
149 149
150 // Don't actually emit anything. 150 // Don't actually emit anything.
151 return null; 151 return null;
152 } 152 }
153 } 153 }
154 154
155 /// Parses setext-style headers. 155 /// Parses setext-style headers.
156 class SetextHeaderSyntax extends BlockSyntax { 156 class SetextHeaderSyntax extends BlockSyntax {
157 const SetextHeaderSyntax(); 157 const SetextHeaderSyntax();
158 158
159 bool canParse(BlockParser parser) { 159 bool canParse(BlockParser parser) {
160 // Note: matches *next* line, not the current one. We're looking for the 160 // Note: matches *next* line, not the current one. We're looking for the
161 // underlining after this line. 161 // underlining after this line.
162 return parser.matchesNext(_RE_SETEXT); 162 return parser.matchesNext(_setextPattern);
163 } 163 }
164 164
165 Node parse(BlockParser parser) { 165 Node parse(BlockParser parser) {
166 final match = _RE_SETEXT.firstMatch(parser.next); 166 var match = _setextPattern.firstMatch(parser.next);
167 167
168 final tag = (match[1][0] == '=') ? 'h1' : 'h2'; 168 var tag = (match[1][0] == '=') ? 'h1' : 'h2';
169 final contents = parser.document.parseInline(parser.current); 169 var contents = parser.document.parseInline(parser.current);
170 parser.advance(); 170 parser.advance();
171 parser.advance(); 171 parser.advance();
172 172
173 return new Element(tag, contents); 173 return new Element(tag, contents);
174 } 174 }
175 } 175 }
176 176
177 /// Parses atx-style headers: `## Header ##`. 177 /// Parses atx-style headers: `## Header ##`.
178 class HeaderSyntax extends BlockSyntax { 178 class HeaderSyntax extends BlockSyntax {
179 RegExp get pattern => _RE_HEADER; 179 RegExp get pattern => _headerPattern;
180 180
181 const HeaderSyntax(); 181 const HeaderSyntax();
182 182
183 Node parse(BlockParser parser) { 183 Node parse(BlockParser parser) {
184 final match = pattern.firstMatch(parser.current); 184 var match = pattern.firstMatch(parser.current);
185 parser.advance(); 185 parser.advance();
186 final level = match[1].length; 186 var level = match[1].length;
187 final contents = parser.document.parseInline(match[2].trim()); 187 var contents = parser.document.parseInline(match[2].trim());
188 return new Element('h$level', contents); 188 return new Element('h$level', contents);
189 } 189 }
190 } 190 }
191 191
192 /// Parses email-style blockquotes: `> quote`. 192 /// Parses email-style blockquotes: `> quote`.
193 class BlockquoteSyntax extends BlockSyntax { 193 class BlockquoteSyntax extends BlockSyntax {
194 RegExp get pattern => _RE_BLOCKQUOTE; 194 RegExp get pattern => _blockquotePattern;
195 195
196 const BlockquoteSyntax(); 196 const BlockquoteSyntax();
197 197
198 Node parse(BlockParser parser) { 198 Node parse(BlockParser parser) {
199 final childLines = parseChildLines(parser); 199 var childLines = parseChildLines(parser);
200 200
201 // Recursively parse the contents of the blockquote. 201 // Recursively parse the contents of the blockquote.
202 final children = parser.document.parseLines(childLines); 202 var children = parser.document.parseLines(childLines);
203 203
204 return new Element('blockquote', children); 204 return new Element('blockquote', children);
205 } 205 }
206 } 206 }
207 207
208 /// Parses preformatted code blocks that are indented four spaces. 208 /// Parses preformatted code blocks that are indented four spaces.
209 class CodeBlockSyntax extends BlockSyntax { 209 class CodeBlockSyntax extends BlockSyntax {
210 RegExp get pattern => _RE_INDENT; 210 RegExp get pattern => _indentPattern;
211 211
212 const CodeBlockSyntax(); 212 const CodeBlockSyntax();
213 213
214 List<String> parseChildLines(BlockParser parser) { 214 List<String> parseChildLines(BlockParser parser) {
215 final childLines = <String>[]; 215 var childLines = <String>[];
216 216
217 while (!parser.isDone) { 217 while (!parser.isDone) {
218 var match = pattern.firstMatch(parser.current); 218 var match = pattern.firstMatch(parser.current);
219 if (match != null) { 219 if (match != null) {
220 childLines.add(match[1]); 220 childLines.add(match[1]);
221 parser.advance(); 221 parser.advance();
222 } else { 222 } else {
223 // If there's a codeblock, then a newline, then a codeblock, keep the 223 // If there's a codeblock, then a newline, then a codeblock, keep the
224 // code blocks together. 224 // code blocks together.
225 var nextMatch = 225 var nextMatch =
226 parser.next != null ? pattern.firstMatch(parser.next) : null; 226 parser.next != null ? pattern.firstMatch(parser.next) : null;
227 if (parser.current.trim() == '' && nextMatch != null) { 227 if (parser.current.trim() == '' && nextMatch != null) {
228 childLines.add(''); 228 childLines.add('');
229 childLines.add(nextMatch[1]); 229 childLines.add(nextMatch[1]);
230 parser.advance(); 230 parser.advance();
231 parser.advance(); 231 parser.advance();
232 } else { 232 } else {
233 break; 233 break;
234 } 234 }
235 } 235 }
236 } 236 }
237 return childLines; 237 return childLines;
238 } 238 }
239 239
240 Node parse(BlockParser parser) { 240 Node parse(BlockParser parser) {
241 final childLines = parseChildLines(parser); 241 var childLines = parseChildLines(parser);
242 242
243 // The Markdown tests expect a trailing newline. 243 // The Markdown tests expect a trailing newline.
244 childLines.add(''); 244 childLines.add('');
245 245
246 // Escape the code. 246 // Escape the code.
247 final escaped = escapeHtml(childLines.join('\n')); 247 var escaped = escapeHtml(childLines.join('\n'));
248 248
249 return new Element('pre', [new Element.text('code', escaped)]); 249 return new Element('pre', [new Element.text('code', escaped)]);
250 } 250 }
251 } 251 }
252 252
253 /// Parses preformatted code blocks between two ~~~ or ``` sequences. 253 /// Parses preformatted code blocks between two ~~~ or ``` sequences.
254 /// [Pandoc's markdown documentation](http://johnmacfarlane.net/pandoc/demo/exam ple9/pandocs-markdown.html). 254 ///
255 /// See [Pandoc's documentation](http://johnmacfarlane.net/pandoc/demo/example9/ pandocs-markdown.html).
255 class FencedCodeBlockSyntax extends BlockSyntax { 256 class FencedCodeBlockSyntax extends BlockSyntax {
256 RegExp get pattern => _RE_CODE; 257 RegExp get pattern => _codePattern;
257 258
258 const FencedCodeBlockSyntax(); 259 const FencedCodeBlockSyntax();
259 260
260 List<String> parseChildLines(BlockParser parser, [String endBlock]) { 261 List<String> parseChildLines(BlockParser parser, [String endBlock]) {
261 if (endBlock == null) endBlock = ''; 262 if (endBlock == null) endBlock = '';
262 263
263 final childLines = <String>[]; 264 var childLines = <String>[];
264 parser.advance(); 265 parser.advance();
266
265 while (!parser.isDone) { 267 while (!parser.isDone) {
266 var match = pattern.firstMatch(parser.current); 268 var match = pattern.firstMatch(parser.current);
267 if (match == null || !match[1].startsWith(endBlock)) { 269 if (match == null || !match[1].startsWith(endBlock)) {
268 childLines.add(parser.current); 270 childLines.add(parser.current);
269 parser.advance(); 271 parser.advance();
270 } else { 272 } else {
271 parser.advance(); 273 parser.advance();
272 break; 274 break;
273 } 275 }
274 } 276 }
277
275 return childLines; 278 return childLines;
276 } 279 }
277 280
278 Node parse(BlockParser parser) { 281 Node parse(BlockParser parser) {
279 // Get the syntax identifier, if there is one. 282 // Get the syntax identifier, if there is one.
280 var match = pattern.firstMatch(parser.current); 283 var match = pattern.firstMatch(parser.current);
281 var endBlock = match.group(1); 284 var endBlock = match.group(1);
282 var syntax = match.group(2); 285 var syntax = match.group(2);
283 286
284 final childLines = parseChildLines(parser, endBlock); 287 var childLines = parseChildLines(parser, endBlock);
285 288
286 // The Markdown tests expect a trailing newline. 289 // The Markdown tests expect a trailing newline.
287 childLines.add(''); 290 childLines.add('');
288 291
289 // Escape the code. 292 // Escape the code.
290 final escaped = escapeHtml(childLines.join('\n')); 293 var escaped = escapeHtml(childLines.join('\n'));
291 294
292 var element = new Element('pre', [new Element.text('code', escaped)]); 295 var element = new Element('pre', [new Element.text('code', escaped)]);
293 if (syntax != '') { 296 if (syntax != '') element.attributes['class'] = syntax;
294 element.attributes['class'] = syntax; 297
295 }
296 return element; 298 return element;
297 } 299 }
298 } 300 }
299 301
300 /// Parses horizontal rules like `---`, `_ _ _`, `* * *`, etc. 302 /// Parses horizontal rules like `---`, `_ _ _`, `* * *`, etc.
301 class HorizontalRuleSyntax extends BlockSyntax { 303 class HorizontalRuleSyntax extends BlockSyntax {
302 RegExp get pattern => _RE_HR; 304 RegExp get pattern => _hrPattern;
303 305
304 const HorizontalRuleSyntax(); 306 const HorizontalRuleSyntax();
305 307
306 Node parse(BlockParser parser) { 308 Node parse(BlockParser parser) {
307 parser.advance(); 309 parser.advance();
308 return new Element.empty('hr'); 310 return new Element.empty('hr');
309 } 311 }
310 } 312 }
311 313
312 /// Parses inline HTML at the block level. This differs from other markdown 314 /// Parses inline HTML at the block level. This differs from other markdown
313 /// implementations in several ways: 315 /// implementations in several ways:
314 /// 316 ///
315 /// 1. This one is way way WAY simpler. 317 /// 1. This one is way way WAY simpler.
316 /// 2. All HTML tags at the block level will be treated as blocks. If you 318 /// 2. All HTML tags at the block level will be treated as blocks. If you
317 /// start a paragraph with `<em>`, it will not wrap it in a `<p>` for you. 319 /// start a paragraph with `<em>`, it will not wrap it in a `<p>` for you.
318 /// As soon as it sees something like HTML, it stops mucking with it until 320 /// As soon as it sees something like HTML, it stops mucking with it until
319 /// it hits the next block. 321 /// it hits the next block.
320 /// 3. Absolutely no HTML parsing or validation is done. We're a markdown 322 /// 3. Absolutely no HTML parsing or validation is done. We're a markdown
321 /// parser not an HTML parser! 323 /// parser not an HTML parser!
322 class BlockHtmlSyntax extends BlockSyntax { 324 class BlockHtmlSyntax extends BlockSyntax {
323 RegExp get pattern => _RE_HTML; 325 RegExp get pattern => _htmlPattern;
324 326
325 bool get canEndBlock => false; 327 bool get canEndBlock => false;
326 328
327 const BlockHtmlSyntax(); 329 const BlockHtmlSyntax();
328 330
329 Node parse(BlockParser parser) { 331 Node parse(BlockParser parser) {
330 final childLines = []; 332 var childLines = <String>[];
331 333
332 // Eat until we hit a blank line. 334 // Eat until we hit a blank line.
333 while (!parser.isDone && !parser.matches(_RE_EMPTY)) { 335 while (!parser.isDone && !parser.matches(_emptyPattern)) {
334 childLines.add(parser.current); 336 childLines.add(parser.current);
335 parser.advance(); 337 parser.advance();
336 } 338 }
337 339
338 return new Text(childLines.join('\n')); 340 return new Text(childLines.join('\n'));
339 } 341 }
340 } 342 }
341 343
342 class ListItem { 344 class ListItem {
343 bool forceBlock = false; 345 bool forceBlock = false;
344 final List<String> lines; 346 final List<String> lines;
345 347
346 ListItem(this.lines); 348 ListItem(this.lines);
347 } 349 }
348 350
349 /// Base class for both ordered and unordered lists. 351 /// Base class for both ordered and unordered lists.
350 abstract class ListSyntax extends BlockSyntax { 352 abstract class ListSyntax extends BlockSyntax {
351 bool get canEndBlock => false; 353 bool get canEndBlock => false;
352 354
353 String get listTag; 355 String get listTag;
354 356
355 const ListSyntax(); 357 const ListSyntax();
356 358
357 Node parse(BlockParser parser) { 359 Node parse(BlockParser parser) {
358 final items = <ListItem>[]; 360 var items = <ListItem>[];
359 var childLines = <String>[]; 361 var childLines = <String>[];
360 362
361 endItem() { 363 endItem() {
362 if (childLines.length > 0) { 364 if (childLines.length > 0) {
363 items.add(new ListItem(childLines)); 365 items.add(new ListItem(childLines));
364 childLines = <String>[]; 366 childLines = <String>[];
365 } 367 }
366 } 368 }
367 369
368 var match; 370 var match;
369 tryMatch(RegExp pattern) { 371 tryMatch(RegExp pattern) {
370 match = pattern.firstMatch(parser.current); 372 match = pattern.firstMatch(parser.current);
371 return match != null; 373 return match != null;
372 } 374 }
373 375
374 while (!parser.isDone) { 376 while (!parser.isDone) {
375 if (tryMatch(_RE_EMPTY)) { 377 if (tryMatch(_emptyPattern)) {
376 // Add a blank line to the current list item. 378 // Add a blank line to the current list item.
377 childLines.add(''); 379 childLines.add('');
378 } else if (tryMatch(_RE_UL) || tryMatch(_RE_OL)) { 380 } else if (tryMatch(_ulPattern) || tryMatch(_olPattern)) {
379 // End the current list item and start a new one. 381 // End the current list item and start a new one.
380 endItem(); 382 endItem();
381 childLines.add(match[1]); 383 childLines.add(match[1]);
382 } else if (tryMatch(_RE_INDENT)) { 384 } else if (tryMatch(_indentPattern)) {
383 // Strip off indent and add to current item. 385 // Strip off indent and add to current item.
384 childLines.add(match[1]); 386 childLines.add(match[1]);
385 } else if (BlockSyntax.isAtBlockEnd(parser)) { 387 } else if (BlockSyntax.isAtBlockEnd(parser)) {
386 // Done with the list. 388 // Done with the list.
387 break; 389 break;
388 } else { 390 } else {
389 // Anything else is paragraph text or other stuff that can be in a list 391 // Anything else is paragraph text or other stuff that can be in a list
390 // item. However, if the previous item is a blank line, this means we're 392 // item. However, if the previous item is a blank line, this means we're
391 // done with the list and are starting a new top-level paragraph. 393 // done with the list and are starting a new top-level paragraph.
392 if ((childLines.length > 0) && (childLines.last == '')) break; 394 if ((childLines.length > 0) && (childLines.last == '')) break;
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
429 // UL, OL) it's a block. (This is for cases like "* > quote".) 431 // UL, OL) it's a block. (This is for cases like "* > quote".)
430 // - If there was a blank line between this item and the previous one, it's 432 // - If there was a blank line between this item and the previous one, it's
431 // a block. 433 // a block.
432 // - If there was a blank line between this item and the next one, it's a 434 // - If there was a blank line between this item and the next one, it's a
433 // block. 435 // block.
434 // - Otherwise, parse it as an inline. 436 // - Otherwise, parse it as an inline.
435 437
436 // Remove any trailing empty lines and note which items are separated by 438 // Remove any trailing empty lines and note which items are separated by
437 // empty lines. Do this before seeing which items are single-line so that 439 // empty lines. Do this before seeing which items are single-line so that
438 // trailing empty lines on the last item don't force it into being a block. 440 // trailing empty lines on the last item don't force it into being a block.
439 for (int i = 0; i < items.length; i++) { 441 for (var i = 0; i < items.length; i++) {
440 for (int j = items[i].lines.length - 1; j > 0; j--) { 442 for (var j = items[i].lines.length - 1; j > 0; j--) {
441 if (_RE_EMPTY.firstMatch(items[i].lines[j]) != null) { 443 if (_emptyPattern.firstMatch(items[i].lines[j]) != null) {
442 // Found an empty line. Item and one after it are blocks. 444 // Found an empty line. Item and one after it are blocks.
443 if (i < items.length - 1) { 445 if (i < items.length - 1) {
444 items[i].forceBlock = true; 446 items[i].forceBlock = true;
445 items[i + 1].forceBlock = true; 447 items[i + 1].forceBlock = true;
446 } 448 }
447 items[i].lines.removeLast(); 449 items[i].lines.removeLast();
448 } else { 450 } else {
449 break; 451 break;
450 } 452 }
451 } 453 }
452 } 454 }
453 455
454 // Convert the list items to Nodes. 456 // Convert the list items to Nodes.
455 final itemNodes = <Node>[]; 457 var itemNodes = <Node>[];
456 for (final item in items) { 458 for (var item in items) {
457 bool blockItem = item.forceBlock || (item.lines.length > 1); 459 var blockItem = item.forceBlock || (item.lines.length > 1);
458 460
459 // See if it matches some block parser. 461 // See if it matches some block parser.
460 final blocksInList = [ 462 var blocksInList = [
461 _RE_BLOCKQUOTE, 463 _blockquotePattern,
462 _RE_HEADER, 464 _headerPattern,
463 _RE_HR, 465 _hrPattern,
464 _RE_INDENT, 466 _indentPattern,
465 _RE_UL, 467 _ulPattern,
466 _RE_OL 468 _olPattern
467 ]; 469 ];
468 470
469 if (!blockItem) { 471 if (!blockItem) {
470 for (final pattern in blocksInList) { 472 for (var pattern in blocksInList) {
471 if (pattern.firstMatch(item.lines[0]) != null) { 473 if (pattern.firstMatch(item.lines[0]) != null) {
472 blockItem = true; 474 blockItem = true;
473 break; 475 break;
474 } 476 }
475 } 477 }
476 } 478 }
477 479
478 // Parse the item as a block or inline. 480 // Parse the item as a block or inline.
479 if (blockItem) { 481 if (blockItem) {
480 // Block list item. 482 // Block list item.
481 final children = parser.document.parseLines(item.lines); 483 var children = parser.document.parseLines(item.lines);
482 itemNodes.add(new Element('li', children)); 484 itemNodes.add(new Element('li', children));
483 } else { 485 } else {
484 // Raw list item. 486 // Raw list item.
485 final contents = parser.document.parseInline(item.lines[0]); 487 var contents = parser.document.parseInline(item.lines[0]);
486 itemNodes.add(new Element('li', contents)); 488 itemNodes.add(new Element('li', contents));
487 } 489 }
488 } 490 }
489 491
490 return new Element(listTag, itemNodes); 492 return new Element(listTag, itemNodes);
491 } 493 }
492 } 494 }
493 495
494 /// Parses unordered lists. 496 /// Parses unordered lists.
495 class UnorderedListSyntax extends ListSyntax { 497 class UnorderedListSyntax extends ListSyntax {
496 RegExp get pattern => _RE_UL; 498 RegExp get pattern => _ulPattern;
497 String get listTag => 'ul'; 499 String get listTag => 'ul';
498 500
499 const UnorderedListSyntax(); 501 const UnorderedListSyntax();
500 } 502 }
501 503
502 /// Parses ordered lists. 504 /// Parses ordered lists.
503 class OrderedListSyntax extends ListSyntax { 505 class OrderedListSyntax extends ListSyntax {
504 RegExp get pattern => _RE_OL; 506 RegExp get pattern => _olPattern;
505 String get listTag => 'ol'; 507 String get listTag => 'ol';
506 508
507 const OrderedListSyntax(); 509 const OrderedListSyntax();
508 } 510 }
509 511
510 /// Parses paragraphs of regular text. 512 /// Parses paragraphs of regular text.
511 class ParagraphSyntax extends BlockSyntax { 513 class ParagraphSyntax extends BlockSyntax {
512 bool get canEndBlock => false; 514 bool get canEndBlock => false;
513 515
514 const ParagraphSyntax(); 516 const ParagraphSyntax();
515 517
516 bool canParse(BlockParser parser) => true; 518 bool canParse(BlockParser parser) => true;
517 519
518 Node parse(BlockParser parser) { 520 Node parse(BlockParser parser) {
519 final childLines = []; 521 var childLines = <String>[];
520 522
521 // Eat until we hit something that ends a paragraph. 523 // Eat until we hit something that ends a paragraph.
522 while (!BlockSyntax.isAtBlockEnd(parser)) { 524 while (!BlockSyntax.isAtBlockEnd(parser)) {
523 childLines.add(parser.current); 525 childLines.add(parser.current);
524 parser.advance(); 526 parser.advance();
525 } 527 }
526 528
527 final contents = parser.document.parseInline(childLines.join('\n')); 529 var contents = parser.document.parseInline(childLines.join('\n'));
528 return new Element('p', contents); 530 return new Element('p', contents);
529 } 531 }
530 } 532 }
OLDNEW
« no previous file with comments | « lib/src/ast.dart ('k') | lib/src/document.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698